library(readxl)
library(caret)
## Loading required package: ggplot2
## Loading required package: lattice
library(tidyverse)         # for graphing and data cleaning
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.1 ──
## ✓ tibble  3.1.6     ✓ dplyr   1.0.7
## ✓ tidyr   1.1.4     ✓ stringr 1.4.0
## ✓ readr   2.1.1     ✓ forcats 0.5.1
## ✓ purrr   0.3.4
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## x dplyr::filter() masks stats::filter()
## x dplyr::lag()    masks stats::lag()
## x purrr::lift()   masks caret::lift()
library(tidymodels)        # for modeling
## Registered S3 method overwritten by 'tune':
##   method                   from   
##   required_pkgs.model_spec parsnip
## ── Attaching packages ────────────────────────────────────── tidymodels 0.1.4 ──
## ✓ broom        0.7.10     ✓ rsample      0.1.1 
## ✓ dials        0.0.10     ✓ tune         0.1.6 
## ✓ infer        1.0.0      ✓ workflows    0.2.4 
## ✓ modeldata    0.1.1      ✓ workflowsets 0.1.0 
## ✓ parsnip      0.1.7      ✓ yardstick    0.0.9 
## ✓ recipes      0.1.17
## ── Conflicts ───────────────────────────────────────── tidymodels_conflicts() ──
## x scales::discard()        masks purrr::discard()
## x dplyr::filter()          masks stats::filter()
## x recipes::fixed()         masks stringr::fixed()
## x dplyr::lag()             masks stats::lag()
## x purrr::lift()            masks caret::lift()
## x yardstick::precision()   masks caret::precision()
## x yardstick::recall()      masks caret::recall()
## x yardstick::sensitivity() masks caret::sensitivity()
## x yardstick::spec()        masks readr::spec()
## x yardstick::specificity() masks caret::specificity()
## x recipes::step()          masks stats::step()
## • Search for functions across packages at https://www.tidymodels.org/find/
library(themis)            # for step functions for unbalanced data
## Registered S3 methods overwritten by 'themis':
##   method                  from   
##   bake.step_downsample    recipes
##   bake.step_upsample      recipes
##   prep.step_downsample    recipes
##   prep.step_upsample      recipes
##   tidy.step_downsample    recipes
##   tidy.step_upsample      recipes
##   tunable.step_downsample recipes
##   tunable.step_upsample   recipes
## 
## Attaching package: 'themis'
## The following objects are masked from 'package:recipes':
## 
##     step_downsample, step_upsample
library(doParallel)        # for parallel processing
## Loading required package: foreach
## 
## Attaching package: 'foreach'
## The following objects are masked from 'package:purrr':
## 
##     accumulate, when
## Loading required package: iterators
## Loading required package: parallel
library(stacks)            # for stacking models
library(naniar)            # for examining missing values (NAs)
library(lubridate)         # for date manipulation
## 
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
## 
##     date, intersect, setdiff, union
library(moderndive)        # for King County housing data
library(vip)               # for variable importance plots
## 
## Attaching package: 'vip'
## The following object is masked from 'package:utils':
## 
##     vi
library(patchwork)         # for combining plots nicely
library(DALEX)
## Welcome to DALEX (version: 2.3.0).
## Find examples and detailed introduction at: http://ema.drwhy.ai/
## Additional features will be available after installation of: ggpubr.
## Use 'install_dependencies()' to get all suggested dependencies
## 
## Attaching package: 'DALEX'
## The following object is masked from 'package:dplyr':
## 
##     explain
library(DALEXtra)
## Anaconda not found on your computer. Conda related functionality such as create_env.R and condaenv and yml parameters from explain_scikitlearn will not be available
library(rpart)
## 
## Attaching package: 'rpart'
## The following object is masked from 'package:dials':
## 
##     prune
library(ggthemes)
theme_set(theme_minimal()) # Lisa's favorite theme

Introduction


Bitcoin, and other cryptocurrencies, have been present for over a decade, and have become increasingly prevalent in the global economic system. Bitcoin, the first and most widely used cryptocurrency, is used as a store of value, tradable asset, and a form of currency. This analysis aims to take advantage of the open-source blockchain network behind Bitcoin, and our goal is to understand the various factors that influence the price movement of Bitcoin.


Bitcoin popularized blockchain technology, and Satoshi Nakamoto used it to solve the Byzanztine’s General Problem and the double-spending problem without the need of a central authority. It is also the fastest growing asset class to have reached close to $2-3 trillion in market capitalization in over a decade. Given the recent technological adoption as seen with Tesla, Microstrategy & El Salvador’s government, we are particularly interested to examine the price movement of Bitcoin by looking at certain variables that are publically accessible through Bitcoin’s public ledger.

Data Sources


We used three different data sources: CoinMetrics, Glassnodes & FRED. We collected an overall of 162 variables with over 4000 observations; however, after basic explanatory analysis and cleaning, we ended up with 88 variables and 2,100 observations. CoinMetrics was the largest bulk of our data, where we used their public API to extract daily data from 2010 to present (November, 2021). We also secured a Glassnodes membership that gave access to daily changes in select variables not present in the CoinMetrics API. Taking from the previous literature review, we also decided to incorporate certain variables that have been examined in previous papers - CPI, Gold Price, Nasdaq & Bitcoin Google Search trends.

Loading the Data

We started with 11 years of data with 4000+ observations, and decided to cut our dataset to the most recent 5-years due to data-discrepancy and since Bitcoin was in its nascent stages earlier.

ADS_Project_11_29_2021_v8 <- read_excel("ADS_Project - 11.29.2021.v8.xlsx")

Data Preprocessing

We filtered through the variables and removed repetitive ones. For example, certain variables were displayed in USD and native units - we removed variables in native units to ensure consistency and standardization throughout. We also removed that overlap - NVT, adjusted, 90d MA & NVT, free float, 90d MA are examples of two that we removed to prevent over fitting.

Master <- ADS_Project_11_29_2021_v8 %>%
select(-c(`Last Calculated EOD metrics (time, in seconds)`, `Block Count`, `Block Size (mean, in bytes)`, `Block Weight, (mean`, `Capitalization of current supply, MVRV`, `Capitalization of free float, MVRV`, `Difficulty, Last Block`, `Difficulty, Mean Block`, `Mean Transaction Fee per Byte of all blocks, native units`, `Mean Transaction Fee per Byte, native units`, `Median Transaction Fee, native units`, `Median Fee per Transacation, USD`, `Addresses balance greater than 0.001 native units`, `Addresses balance greater than 0.01 native units`, `Addresses balance greater than 0.1 native units`,`Addresses balance greater than 100 native units`, `Addresses balance greater than 100K native units`, `Addresses balance greater than 10 native units`, `Addresses balance greater than 10K native units`, `Addresses balance greater than 1 native units`, `Addresses balance greater than 1K native units`, `Addresses balance greater than 1M native units`, `Addresses balance greater than 1in100K`, `Addresses balance greater than 1in100M`, `Addresses balance greater than 1in10B`, `Addresses balance greater than 1in10K`, `Addresses balance greater than 1in10K`, `Addresses balance greater than 1in10M`, `Addresses balance greater than 1in1B`, `Addresses balance greater than 1in1K`, `Addressesbalance greater than 1in1M`, `Total Fees, native units`, `Flow to exchanges, native units`, `Flow out exchanges, native units`, `Gas Limit Block`, `Gas Limit, transaction`, `Gas Used, transaction`, `Hash Rate Mean, 30d`, `Issuance, native units`, `Issuance, percent annualized`, `Issuance,  percent daily`, `Issuance Total, native units`,`NVT, adjusted, 90d MA`, `NVT, adjusted, free float,  90d MA`, `ROI, 1y (%)`, `Revenue, per hash unit (Native Currency)`, `Revenue Daily, per hash unit/second (Native Currency)`, `Miner Rev (Native Currency)`, `Supply, 10y Active`, `Supply, 5yr Active`, `Supply, 4yr Active`, `Supply, 3yr Active`, `Supply, 1yr Active Trailing (%)`, `Supply, in addresses (<0.001, native currency)`, `Supply, in addresses (<0.01M, native currency)`, `Supply, in addresses (<0.1, native currency)`, `Supply, in addresses (<1, native currency)`, `Supply, in addresses (<10, native currency)`, `Supply, in addresses (<100, native currency)`, `Supply, in addresses (<100K, native currency)`, `Supply, in addresses (<10K, native currency)`, `Supply, in addresses (<1K, native currency)`, `Supply, in addresses (<1M, native currency)`, `Supply, Miner address with 1 mining entity (native currency)`, `Supply, All Miners (native currency)`, `Transactions, adjusted transfer value (native currency)`, `Transactions, mean transfer value (native currency)`, `Transactions, median transfer value (native currency)`, `Transactions, mean transfer value ($)`, `Transactions, median transfer value (native currency)`, `Velocity, 1yr current supply`, `Volatility, 180d daily returns`, `Volatility, 30d daily returns`, `Price, BTC`, `Price, ($)`, `Transactions, median transfer value ($)`))

We arrived at 88 variables and 2145 observations. We then plotted over 60 graphs and removed certain variables that either did not have any correlation to the output or where other variables had a visually overlapping effect. Below are plots of select variables that we removed during this process.

ggplot(Master,
       aes(x = `Hash Rate, mean`,
           y = `Price`)) + 
         geom_point() + 
         geom_smooth(method = lm, se = FALSE) + theme_economist()
## `geom_smooth()` using formula 'y ~ x'

ggplot(Master,
       aes(x = `Issuance, in USD`,
           y = `Price`)) + 
         geom_point() + 
         geom_smooth(method = lm, se = FALSE) + theme_economist()
## `geom_smooth()` using formula 'y ~ x'

ggplot(Master,
       aes(x = `Issuance Total, in USD`,
           y = `Price`)) + 
         geom_point() + 
         geom_smooth(method = lm, se = FALSE) + theme_economist()
## `geom_smooth()` using formula 'y ~ x'

ggplot(Master,
       aes(x = `Supply, 30D Active`,
           y = `Price`)) + 
         geom_point() + 
         geom_smooth(method = lm, se = FALSE) + theme_economist()
## `geom_smooth()` using formula 'y ~ x'

ggplot(Master,
       aes(x = `Supply, 7D Active`,
           y = `Price`)) + 
         geom_point() + 
         geom_smooth(method = lm, se = FALSE) + theme_economist()
## `geom_smooth()` using formula 'y ~ x'

ggplot(Master,
       aes(x = `Supply, 90D Active`,
           y = `Price`)) + 
         geom_point() + 
         geom_smooth(method = lm, se = FALSE)+ theme_economist()
## `geom_smooth()` using formula 'y ~ x'


Consequently, the graphical explandatory data analysis showed certain variables that demonstrated a strong corelation to the output. Some of these variables can be seen below.

ggplot(Master,
       aes(x = `Gas Limit Block, mean`,
           y = `Price`)) + 
         geom_point() + 
         geom_smooth(method = lm, se = FALSE) + theme_economist()
## `geom_smooth()` using formula 'y ~ x'

ggplot(Master,
       aes(x = `NVT, adjusted`,
           y = `Price`)) + 
         geom_point() + 
         geom_smooth(method = lm, se = FALSE) +
   theme_economist()
## `geom_smooth()` using formula 'y ~ x'

ggplot(Master,
       aes(x = `Supply, in addresses (<$10M)`,
           y = `Price`)) + 
         geom_point() + 
         geom_smooth(method = lm, se = FALSE) +
   theme_economist()
## `geom_smooth()` using formula 'y ~ x'

The code below shows the variables we removed after plotting most of the graphs as seen above. This further dropped our variable count from 88 variables to 27.

Master <- Master %>% select(-c(`Gas limit Mean, transaction`, `Hash Rate, mean`, `Issuance, in USD`, `Issuance Total, in USD`, `ROI, 30D (%)`, `Revenue Daily, per hash unit/second (USD)`, `Revenue, per hash unit ($)`, `Supply Equality Ratio`, `Supply, 180D Active`, `Supply, 1yr Active`, `Supply, 2y Active`, `Supply, 1yr Active`, `Supply, Miner address with 1 mining entity ($)`, `Transactions, count`,`Transactions, count (per second)`, `Transactions, adjusted transfer value ($)`, `Coin_Days_Destroyed`, `Balance_Exchanges_(%)`, `Total_Exchange_Inflow_Volume`, `Supply, 30D Active`, `Supply, 7D Active`, `Supply, 90D Active`, `Supply, address balance (<1in10K)`, `Supply, address balance (<1in10M)`, `Supply, address balance (<1in10B)`, `Supply, address balance (<1in10K)`, `Supply, address balance (<1in1B)`, `Supply, address balance (<1in1K)`, `Supply, address balance (<1in1M)`, `Supply, in addresses (<$1)`, `Supply, in addresses (<$10)`, `Supply, in addresses (<$100)`, `Supply, in addresses (<$100K)`, `Supply, in addresses (<$10K)`, `Supply, in addresses (<$10M)`, `Supply, in addresses (<$1K)`, `Supply, held by top 1% addresses`, `Supply, 10yr expected supply`, `Supply, free float`, `Supply, All Miners ($)`, `Supply, Miner address with 1 mining entity ($)`, `Transactions, count`, `Transactions, adjusted transfer value ($)`, `Coin_Days_Destroyed`, `CPI_(%)`))
Master <- Master %>% select(-c(`All Addresses balance`, `Addresses balance greater than $100`, `Addresses balance greater than $10`, `Addresses balance greater than $1`, `Capitalization of active native units supply, in USD`, `Capitalization of free float, in USD`, `Capitalization realized, USD`, `NVT, adjusted, free float`, `Miner Rev ($)`, `Supply, current`, `Flow transfers exchanges`, `Supply, address balance (<1in100M)`, `Supply, in addresses (<$1M)`, `Transactions, transfer count`, `NUPL`, `rHODL`, `Addresses Active Count`,`Addresses balance greater than $10K`, `Addresses balance greater than $100K`, `Addresses balance greater than $1K`, `Addresses balance greater than $1M`, `Block, weight, total`))

In the section below, we carry out further pre-processing. We start by converting the Date into numeric format from character, so this will not cause problems when building the recipes for the models moving forward. We also introduce a few new variables which represent the lagged characteristics of the Prices such as lag1day which essentially enables each observation to obtain more than 1 price variable; hence the Price variable will represent that specific days price and lag1day will represent the previous day’s price.

 Master <- Master %>%
           mutate(Date = ymd(Date)) %>% 
           mutate(Date_num = as.numeric(Date)) %>%
           mutate(lag1day = lag(Price, 1, order_by = Date),
                  lag7day = lag(Price, 7, order_by = Date)) %>%
           replace_na(list(lag1day = 0, lag7day = 0)) %>% 
           mutate(Daily_avg = (Price + lag1day)/2,
                  Weekly_avg = (Price + lag7day)/7) %>%
           select(-c(CPI, `BTC(%)`, `NDQ(%)`, `Gold(%)`, BTC_Searches, `BTCS(%)`))

Machine Learning Models

We make use of three core models in our analysis — + Lasso Classification + Random Forest + Decision Trees

We also carry out further exploratory analysis with Support Vector Machines to see how it performs

Lasso - Binary Classification

Initially, we wanted to use a lasso predictive regression which would model a quantitative Y variable (Price). Upon preliminary model building and testing, we observed that the model performed very poorly with this approach. This was likely due to Bitcoin having extremely volatile prices creating increased variation and noise which made modelling it accurately difficult. Hence, we moved forward with a binary lasso classification.

In building the Lasso, we created a new variable called PriceDirection which would allow us to look at price actions in a binary structure with the help of the lagged variables. We define the main dependent variable by passing on a logical condition whereby if Price is greater than lag1day, then the condition is true and represents a price increase (1). If the condition fails, it will translate to a price decrease (0). This is shown below.

Master <- Master %>% mutate(Date = ymd(Date)) %>% 
           mutate(Date_num = as.numeric(Date)) %>%
           mutate(PriceDirection = ifelse(Price > lag1day, yes = 1, no = 0)) %>%
           mutate(PriceDirection = as.factor(PriceDirection)) 

We take a proportional split of 3/4 for the split between Train and Test, whereby 0.75 is attributed to the training set and 0.25 is used for testing. Given the rapid acceleration in the price of Bitcoin over the years, we decided not to use a time split.

set.seed(456)
Master_Split <- initial_split(Master, prop = 0.75)

Master_Training <- training(Master_Split)
Master_Testing <- testing(Master_Split)

In the next step, we prepare the Lasso model recipe. We remove Date since the recipe building can only process numeric data structures. We also exclude Network Distribution Factor, Capitalization of Current Supply, in USD, and Supply, held by top 10% addresses as these variables demonstrated issues of Endogeneity. We also exclude other variables which are not used for this section.

lasso_recipe <- recipe(PriceDirection ~ .,
                       data = Master_Training) %>%
                step_rm(Date, Weekly_avg, Daily_avg, lag7day, lag1day, `Network distribution factor`, `Capitalization of Current Supply, in USD`, `Supply, held by top 10% addresses`) %>%
                step_mutate_at(all_numeric(), -all_outcomes(), fn= ~ as.numeric(.)) %>%
                step_dummy(all_nominal(), -all_outcomes()) %>%
                update_role(Date_num, new_role = "ID") %>%
                step_zv(all_predictors()) %>%
                step_normalize(all_predictors(), -all_nominal())

lasso_recipe %>% prep() %>% juice()
lasso_mod <- logistic_reg(mixture = 1) %>%
             set_engine("glmnet") %>% 
             set_args(penalty = tune()) %>% 
             set_mode("classification")

lasso_wf <- workflow() %>%
            add_recipe(lasso_recipe) %>%
            add_model(lasso_mod) 
set.seed(494)
btc_cv <- vfold_cv(Master_Training,
                   v = 10)

penalty_grid <- grid_regular(penalty(),
                             levels = 10)

ctrl_grid <- control_stack_resamples()

metric <- metric_set(accuracy)
lasso_tune <- lasso_wf %>%
              tune_grid(resamples = btc_cv,
                        grid = penalty_grid,
                        control = ctrl_grid)

lasso_tune %>% collect_metrics()
best_param <- lasso_tune %>%
              select_best(metric = "accuracy")
best_param
final_lasso <- lasso_wf %>% finalize_workflow(best_param) %>%
               fit(data = Master_Training)

final_lasso %>% pull_workflow_fit() %>% tidy()
## Warning: `pull_workflow_fit()` was deprecated in workflows 0.2.3.
## Please use `extract_fit_parsnip()` instead.
library(vip)
final_lasso %>% 
      fit(Master_Training) %>%
      extract_fit_parsnip() %>%
      vi(lamda = lowest_rmse$penalty) %>%
      mutate(Importance = abs(Importance),
             Variable = fct_reorder(Variable, Importance)) %>%
     ggplot(aes(x = Importance, y = Variable, fill = Sign)) +
     geom_col() + 
     scale_x_continuous(expand = c(0,0)) +
     labs(y = NULL)

lasso_fit <- final_lasso %>%
  last_fit(Master_Split)

lasso_fit
test_performance_lasso <- lasso_fit %>% collect_metrics()
test_performance_lasso

The Lasso classification model selects the variables based in the magnitude of their coefficients. We built the Lasso by using a penalty grid level of 10, CV resampling of 10 folds, and moved forward by selecting the tuning parameter with the highest prediction accuracy as the parameter for the final model. As shown above, the prediction accuracy for the lasso model is 59.4%, predicting the correct price movement 59.4% of the time with Receiver operating characteristic area under curve of 62%.

Random Forest - Classification

The next model we use is a Random Forest. Similar to the lasso model, we take most of the same concepts and ideas in building the model with PriceDirection being the main dependent variable. We adjust for the removal of the variables we excluded in the lasso for the same reasons as above.

We build our random forest model using 10 trees with penalty grid of level 3 to tune the forest and a resampling CV of 10 folds. The model returns the the mode of the predictions for the 10 trees.

set.seed(456)
rf_recipe <- recipe(PriceDirection ~.,
                    data = Master_Training) %>%
             step_mutate_at(all_numeric(), fn = ~as.numeric(.)) %>%
             step_rm(Weekly_avg, Daily_avg, lag7day, lag1day, `Network distribution factor`, `Capitalization of Current Supply, in USD`, `Supply, held by top 10% addresses`) 

rf_recipe %>% prep() %>% juice()
rf_model <- rand_forest(mtry = tune(),
                        min_n = tune(),
                        trees = 10) %>%
            set_mode("classification") %>%
            set_engine("ranger")

rf_workflow <- workflow() %>%
               add_recipe(rf_recipe) %>%
               add_model(rf_model)

rf_penalty_grid <- grid_regular(finalize(mtry(),
                                Master_Training %>%
                                select(-PriceDirection)),
                                min_n(),
                                levels = 2)

rf_tune <- rf_workflow %>%
           tune_grid(resamples = btc_cv,
                     grid = rf_penalty_grid,
                     control = control_stack_grid())
## ! Fold01: preprocessor 1/1, model 2/4: 26 columns were requested but there were 19...
## ! Fold01: preprocessor 1/1, model 4/4: 26 columns were requested but there were 19...
## ! Fold02: preprocessor 1/1, model 2/4: 26 columns were requested but there were 19...
## ! Fold02: preprocessor 1/1, model 4/4: 26 columns were requested but there were 19...
## ! Fold03: preprocessor 1/1, model 2/4: 26 columns were requested but there were 19...
## ! Fold03: preprocessor 1/1, model 4/4: 26 columns were requested but there were 19...
## ! Fold04: preprocessor 1/1, model 2/4: 26 columns were requested but there were 19...
## ! Fold04: preprocessor 1/1, model 4/4: 26 columns were requested but there were 19...
## ! Fold05: preprocessor 1/1, model 2/4: 26 columns were requested but there were 19...
## ! Fold05: preprocessor 1/1, model 4/4: 26 columns were requested but there were 19...
## ! Fold06: preprocessor 1/1, model 2/4: 26 columns were requested but there were 19...
## ! Fold06: preprocessor 1/1, model 4/4: 26 columns were requested but there were 19...
## ! Fold07: preprocessor 1/1, model 2/4: 26 columns were requested but there were 19...
## ! Fold07: preprocessor 1/1, model 4/4: 26 columns were requested but there were 19...
## ! Fold08: preprocessor 1/1, model 2/4: 26 columns were requested but there were 19...
## ! Fold08: preprocessor 1/1, model 4/4: 26 columns were requested but there were 19...
## ! Fold09: preprocessor 1/1, model 2/4: 26 columns were requested but there were 19...
## ! Fold09: preprocessor 1/1, model 4/4: 26 columns were requested but there were 19...
## ! Fold10: preprocessor 1/1, model 2/4: 26 columns were requested but there were 19...
## ! Fold10: preprocessor 1/1, model 4/4: 26 columns were requested but there were 19...
rf_tune %>% collect_metrics()
best_param_rf <- rf_tune %>%
              select_best(metric = "accuracy")
best_param_rf
rf_final <- rf_workflow %>% finalize_workflow(best_param_rf)
rf_fit <- rf_final %>%
  last_fit(Master_Split)
## ! train/test split: preprocessor 1/1, model 1/1: 26 columns were requested but there were 19...
rf_fit
test_performance_rf <- rf_fit %>% collect_metrics()
test_performance_rf

We observe that the random forest performs poorly compared to the lasso model. Our best performing random forest model with the best tuned parameter has a prediction accuracy of 52.3% when tested against the test split. The highest prediction accuracy is seen for tuning parameter mtry = 26 and min_n = 2 with a prediction accuracy of 56.3%.

Decision tree

The last model we use to create a stacked model is a decision tree. We utilize the same recipe built for the random forest to build the tree model with the same parameters. The results we obtained below were suprising because the tree model outperforms the random forest model with model prediction accuracy of 59.8% and test accuracy of 60%. The tree model delivers the highest prediction accuracy for testing among all the three models.

#decision trees
set.seed(456)

tree_model <-
  decision_tree() %>%
  set_mode("classification") %>%
  set_engine("rpart")

tree_workflow <-
  workflow() %>%
  add_recipe(rf_recipe) %>%  
  add_model(tree_model)

tree_fit <-
  tree_workflow %>%
  fit_resamples(btc_cv,
                control = control_stack_resamples()
  )

collect_metrics(tree_fit)
best_param_tree <- tree_fit %>%
              select_best(metric = "accuracy")
best_param_tree
tree_final <- tree_workflow %>% finalize_workflow(best_param_rf)

Tree_fit <- tree_final %>%
  last_fit(Master_Split)

Tree_fit
test_performance_tree <- Tree_fit %>% collect_metrics()
test_performance_tree

Stacking

lasso_tune %>%
  collect_metrics()
rf_tune %>%
  collect_metrics()
tree_fit %>%
  collect_metrics()
Models_stack <-
  stacks() %>%
  add_candidates(lasso_tune) %>%
  add_candidates(rf_tune) %>%
  add_candidates(tree_fit)
Models_blend <-
  Models_stack %>%
  blend_predictions()
Models_blend
## ── A stacked ensemble model ─────────────────────────────────────
## 
## Out of 10 possible candidate members, the ensemble retained 5.
## Penalty: 0.01.
## Mixture: 1.
## 
## The 5 highest weighted member classes are:
## # A tibble: 5 × 3
##   member                  type          weight
##   <chr>                   <chr>          <dbl>
## 1 .pred_1_lasso_tune_1_07 logistic_reg  2.10  
## 2 .pred_1_lasso_tune_1_08 logistic_reg  0.930 
## 3 .pred_1_tree_fit_1_1    decision_tree 0.898 
## 4 .pred_1_rf_tune_1_2     rand_forest   0.110 
## 5 .pred_1_rf_tune_1_4     rand_forest   0.0553
## 
## Members have not yet been fitted with `fit_members()`.
Model_final_stack <- Models_blend %>%
  fit_members() 

Data Visualization

Master %>% 
  select(`Price`, `Date_num`, `SOPR`, `Adjusted_Dormancy_Flow`) %>%
  pivot_longer(cols=-`Date_num`, names_to = "variable", values_to = "values") %>% 
  ggplot(aes(`Date_num`, `values`)) + geom_line() + facet_wrap(vars(`variable`), ncol = 1, scales = "free_y") + theme_wsj()

This plot shows the dormancy flows of dormant coins throughout a bitcoin cycle. For example, at the peak of the cycle in 2018 (Date_num < 17500), most of the the dormant coins were being trasacted and tranferred across changes. It comes to show how in the current cycle the dormant coins have not reached that level of transactions and movement. The SOPR, on the other hands, helps understand short-term price movement. During large dumps, like March 2020 (Date_num < 18500), we see how the SOPR drops significantly around 0.9, while it reaches closer to 1.10 at bull market local peaks.

Master %>% 
  select(`Price`, `Date_num`, `Supply, held by top 100 addresses`, `Supply, Total Active`) %>%
  pivot_longer(cols=-`Date_num`, names_to = "variable", values_to = "values") %>% 
  ggplot(aes(`Date_num`, `values`)) + geom_line() + facet_wrap(vars(`variable`), ncol = 1, scales = "free_y") + theme_wsj()

One of the interesting variables to examine is Supply, held by the top 100 addresses. These are the largest bitcoin holders, and their holdings often dictate the price direction of Bitcoin - this is primarily due to the fact that they have experienced multiple cycles, can anticipate how the current cycle may play out, and potentially because they hold greater information about upcoming price catalysts. Them increasing their holdings significantly during the March 2020 dump could be a potential indicator to frontrun a large increase in the price of bitcoin.

Master %>% 
  select(`Price`, `Date_num`, `Coin_Years_Destroyed`, `NVT, adjusted`) %>%
  pivot_longer(cols=-`Date_num`, names_to = "variable", values_to = "values") %>% 
  ggplot(aes(`Date_num`, `values`)) + geom_line() + facet_wrap(vars(`variable`), ncol = 1, scales = "free_y") + theme_wsj()

The Network Value to Transaction helps understand the relationship between transfer volume and the market cap of Bitcoin. In order words, a higher NVT indicates that the valuation of the Bitcoin network is greater than the transaction volume (a more intrinsic value indicator). The blow-off top in 2018 could be anticipated by the sudden increase in NVT, which would indicate a bubble-like behavior in terms of Bitcoin’s transaction volume vs it’s market cap.

Below are additional graphs plotted with the price of Bitcoin over the span of 5 years.

Master %>% 
    select(`Price`, `Date_num`,`Miner Rev, Total ($)`, `Total Fees, USD`) %>%
  pivot_longer(cols=-`Date_num`, names_to = "variable", values_to = "values") %>% 
  ggplot(aes(`Date_num`, `values`)) + geom_line() + facet_wrap(vars(`variable`), ncol = 1, scales = "free_y") + theme_wsj()

Master %>% 
  select(`Price`, `Date_num`, `Gas Limit Block, mean`, `Addresses balance greater than $10M`) %>%
  pivot_longer(cols=-`Date_num`, names_to = "variable", values_to = "values") %>% 
  ggplot(aes(`Date_num`, `values`)) + geom_line() + facet_wrap(vars(`variable`), ncol = 1, scales = "free_y") + theme_wsj()

Master %>% 
    select(`Price`, `Date_num`,`Flow out exchanges, USD`, `Mean Fee per Transaction, USD`) %>%
  pivot_longer(cols=-`Date_num`, names_to = "variable", values_to = "values") %>% 
  ggplot(aes(`Date_num`, `values`)) + geom_line() + facet_wrap(vars(`variable`), ncol = 1, scales = "free_y") + theme_wsj()

Data Dictionary

Spent Output Profit Ratio (SOPR): Price Paid / Price Sold Gas Limit Block, Mean: Sum gas limit of all block from that interval Adjusted Dormancy Flow: Market Cap/Annualized Dormancy Value Coin Years Active: Coins Transacted / # of Days Since Last Transacted NVT, Adjust: Market Cap/Transfer Volume

Supplementary Model

SVM - Classification

We also carry out supplementary analysis with Support Vector Machines because of their advantages in classification tasks. We observe that for the main Support vector machine model we built, the prediction accuracy does not differ by a considerable rate from the three models we have used above.

Master_2 <- Master %>%
            mutate("PriceDirection" = factor(ifelse(PriceDirection == "1" | PriceDirection != "0",
                                                    "Price_Increase", "Price_Decrease"),
                                                    levels = c("Price_Increase", "Price_Decrease")))
Master_Split2 <- initial_split(Master_2, prop = 0.75)

Master_Training2 <- training(Master_Split2)
Master_Testing2 <- testing(Master_Split2)
btc_cv2 <- vfold_cv(Master_Training2,
                   v = 10)
svm_rec <- 
  recipe(PriceDirection ~ ., data = Master_2) 

svm_spec <-
  svm_rbf(cost = tune(), rbf_sigma = tune()) %>%
  set_mode("classification") %>%
  set_engine("kernlab")
svm_wflow <- 
  workflow() %>% 
  add_model(svm_spec) %>% 
  add_recipe(svm_rec)
cost()
## Cost (quantitative)
## Transformer:  log-2 
## Range (transformed scale): [-10, 5]
rbf_sigma()
## Radial Basis Function sigma (quantitative)
## Transformer:  log-10 
## Range (transformed scale): [-10, 0]
svm_param <- 
  svm_wflow %>% 
  parameters() %>% 
  update(rbf_sigma = rbf_sigma(c(-7, -1)))

start_grid <- 
  svm_param %>% 
  update(
    cost = cost(c(-6, 1)),
    rbf_sigma = rbf_sigma(c(-6, -4))) %>% 
  grid_regular(levels = 2)

set.seed(456)

svm_initial <- 
  svm_wflow %>% 
  tune_grid(resamples = btc_cv2, 
            grid = start_grid, 
            control = control_stack_grid())
collect_metrics(svm_initial)
best_param_svm <- svm_initial %>%
              select_best(metric = "accuracy")
best_param_svm
Bayesian Optimization for SVM
ctrl <- control_bayes(verbose = TRUE)

set.seed(420)

svm_bo <-
  svm_wflow %>%
  tune_bayes(
    resamples = btc_cv2, 
    metrics = NULL, 
    initial = svm_initial, # tune_grid object produced earlier
    param_info = svm_param, # specified earlier too, with our new bounds for rbf_sigma
    iter = 27, # maximum number of search iterations
    control = ctrl
  )
## Optimizing roc_auc using the expected improvement
## 
## ── Iteration 1 ─────────────────────────────────────────────────────────────────
## 
## i Current best:      roc_auc=0.5997 (@iter 0)
## i Gaussian process model
## ✓ Gaussian process model
## i Generating 5000 candidates
## i Predicted candidates
## i cost=0.00255, rbf_sigma=0.00011
## i Estimating performance
## ✓ Estimating performance
## ⓧ Newest results:    roc_auc=0.5991 (+/-0.0129)
## 
## ── Iteration 2 ─────────────────────────────────────────────────────────────────
## 
## i Current best:      roc_auc=0.5997 (@iter 0)
## i Gaussian process model
## ✓ Gaussian process model
## i Generating 5000 candidates
## i Predicted candidates
## i cost=0.000999, rbf_sigma=2.29e-06
## i Estimating performance
## ✓ Estimating performance
## ⓧ Newest results:    roc_auc=0.5939 (+/-0.0137)
## 
## ── Iteration 3 ─────────────────────────────────────────────────────────────────
## 
## i Current best:      roc_auc=0.5997 (@iter 0)
## i Gaussian process model
## ✓ Gaussian process model
## i Generating 5000 candidates
## i Predicted candidates
## i cost=14.3, rbf_sigma=0.000296
## i Estimating performance
## ✓ Estimating performance
## ♥ Newest results:    roc_auc=0.6046 (+/-0.0141)
## 
## ── Iteration 4 ─────────────────────────────────────────────────────────────────
## 
## i Current best:      roc_auc=0.6046 (@iter 3)
## i Gaussian process model
## ✓ Gaussian process model
## i Generating 5000 candidates
## i Predicted candidates
## i cost=4.2, rbf_sigma=0.00123
## i Estimating performance
## ✓ Estimating performance
## ♥ Newest results:    roc_auc=0.6052 (+/-0.0133)
## 
## ── Iteration 5 ─────────────────────────────────────────────────────────────────
## 
## i Current best:      roc_auc=0.6052 (@iter 4)
## i Gaussian process model
## ✓ Gaussian process model
## i Generating 5000 candidates
## i Predicted candidates
## i cost=31.1, rbf_sigma=0.00299
## i Estimating performance
## ✓ Estimating performance
## ♥ Newest results:    roc_auc=0.6536 (+/-0.0129)
## 
## ── Iteration 6 ─────────────────────────────────────────────────────────────────
## 
## i Current best:      roc_auc=0.6536 (@iter 5)
## i Gaussian process model
## ✓ Gaussian process model
## i Generating 5000 candidates
## i Predicted candidates
## i cost=18.9, rbf_sigma=0.0039
## i Estimating performance
## ✓ Estimating performance
## ⓧ Newest results:    roc_auc=0.6426 (+/-0.0124)
## 
## ── Iteration 7 ─────────────────────────────────────────────────────────────────
## 
## i Current best:      roc_auc=0.6536 (@iter 5)
## i Gaussian process model
## ✓ Gaussian process model
## i Generating 5000 candidates
## i Predicted candidates
## i cost=1.02, rbf_sigma=0.00282
## i Estimating performance
## ✓ Estimating performance
## ⓧ Newest results:    roc_auc=0.5977 (+/-0.0128)
## 
## ── Iteration 8 ─────────────────────────────────────────────────────────────────
## 
## i Current best:      roc_auc=0.6536 (@iter 5)
## i Gaussian process model
## ✓ Gaussian process model
## i Generating 5000 candidates
## i Predicted candidates
## i cost=31.7, rbf_sigma=0.00229
## i Estimating performance
## ✓ Estimating performance
## ⓧ Newest results:    roc_auc=0.6499 (+/-0.0131)
## 
## ── Iteration 9 ─────────────────────────────────────────────────────────────────
## 
## i Current best:      roc_auc=0.6536 (@iter 5)
## i Gaussian process model
## ✓ Gaussian process model
## i Generating 5000 candidates
## i Predicted candidates
## i cost=29.5, rbf_sigma=0.00282
## i Estimating performance
## ✓ Estimating performance
## ⓧ Newest results:    roc_auc=0.6507 (+/-0.0131)
## 
## ── Iteration 10 ────────────────────────────────────────────────────────────────
## 
## i Current best:      roc_auc=0.6536 (@iter 5)
## i Gaussian process model
## ✓ Gaussian process model
## i Generating 5000 candidates
## i Predicted candidates
## i cost=31.2, rbf_sigma=0.0673
## i Estimating performance
## ✓ Estimating performance
## ♥ Newest results:    roc_auc=0.7011 (+/-0.0124)
## 
## ── Iteration 11 ────────────────────────────────────────────────────────────────
## 
## i Current best:      roc_auc=0.7011 (@iter 10)
## i Gaussian process model
## ✓ Gaussian process model
## i Generating 5000 candidates
## i Predicted candidates
## i cost=31.7, rbf_sigma=0.069
## i Estimating performance
## ✓ Estimating performance
## ♥ Newest results:    roc_auc=0.7014 (+/-0.0124)
## 
## ── Iteration 12 ────────────────────────────────────────────────────────────────
## 
## i Current best:      roc_auc=0.7014 (@iter 11)
## i Gaussian process model
## ✓ Gaussian process model
## i Generating 5000 candidates
## i Predicted candidates
## i cost=31.4, rbf_sigma=0.0899
## i Estimating performance
## ✓ Estimating performance
## ⓧ Newest results:    roc_auc=0.6937 (+/-0.012)
## 
## ── Iteration 13 ────────────────────────────────────────────────────────────────
## 
## i Current best:      roc_auc=0.7014 (@iter 11)
## i Gaussian process model
## ✓ Gaussian process model
## i Generating 5000 candidates
## i Predicted candidates
## i cost=30.9, rbf_sigma=0.0447
## i Estimating performance
## ✓ Estimating performance
## ♥ Newest results:    roc_auc=0.7058 (+/-0.0109)
## 
## ── Iteration 14 ────────────────────────────────────────────────────────────────
## 
## i Current best:      roc_auc=0.7058 (@iter 13)
## i Gaussian process model
## ✓ Gaussian process model
## i Generating 5000 candidates
## i Predicted candidates
## i cost=31.5, rbf_sigma=0.0366
## i Estimating performance
## ✓ Estimating performance
## ♥ Newest results:    roc_auc=0.7075 (+/-0.0101)
## 
## ── Iteration 15 ────────────────────────────────────────────────────────────────
## 
## i Current best:      roc_auc=0.7075 (@iter 14)
## i Gaussian process model
## ✓ Gaussian process model
## i Generating 5000 candidates
## i Predicted candidates
## i cost=0.00101, rbf_sigma=0.0911
## i Estimating performance
## ✓ Estimating performance
## ⓧ Newest results:    roc_auc=0.5814 (+/-0.0114)
## 
## ── Iteration 16 ────────────────────────────────────────────────────────────────
## 
## i Current best:      roc_auc=0.7075 (@iter 14)
## i Gaussian process model
## ✓ Gaussian process model
## i Generating 5000 candidates
## i Predicted candidates
## i cost=31.7, rbf_sigma=0.0281
## i Estimating performance
## ✓ Estimating performance
## ⓧ Newest results:    roc_auc=0.7058 (+/-0.00991)
## 
## ── Iteration 17 ────────────────────────────────────────────────────────────────
## 
## i Current best:      roc_auc=0.7075 (@iter 14)
## i Gaussian process model
## ✓ Gaussian process model
## i Generating 5000 candidates
## i Predicted candidates
## i cost=0.094, rbf_sigma=0.0996
## i Estimating performance
## ✓ Estimating performance
## ⓧ Newest results:    roc_auc=0.5823 (+/-0.012)
## 
## ── Iteration 18 ────────────────────────────────────────────────────────────────
## 
## i Current best:      roc_auc=0.7075 (@iter 14)
## i Gaussian process model
## ✓ Gaussian process model
## i Generating 5000 candidates
## i Predicted candidates
## i cost=30.7, rbf_sigma=1.08e-07
## i Estimating performance
## ✓ Estimating performance
## ⓧ Newest results:    roc_auc=0.6013 (+/-0.0116)
## 
## ── Iteration 19 ────────────────────────────────────────────────────────────────
## 
## i Current best:      roc_auc=0.7075 (@iter 14)
## i Gaussian process model
## ✓ Gaussian process model
## i Generating 5000 candidates
## i Predicted candidates
## i cost=31.8, rbf_sigma=0.0345
## i Estimating performance
## ✓ Estimating performance
## ♥ Newest results:    roc_auc=0.7076 (+/-0.0102)
## 
## ── Iteration 20 ────────────────────────────────────────────────────────────────
## 
## i Current best:      roc_auc=0.7076 (@iter 19)
## i Gaussian process model
## ✓ Gaussian process model
## i Generating 5000 candidates
## i Predicted candidates
## i cost=31.9, rbf_sigma=0.0329
## i Estimating performance
## ✓ Estimating performance
## ♥ Newest results:    roc_auc=0.7078 (+/-0.00976)
## 
## ── Iteration 21 ────────────────────────────────────────────────────────────────
## 
## i Current best:      roc_auc=0.7078 (@iter 20)
## i Gaussian process model
## ✓ Gaussian process model
## i Generating 5000 candidates
## i Predicted candidates
## i cost=0.00121, rbf_sigma=1.04e-07
## i Estimating performance
## ✓ Estimating performance
## ⓧ Newest results:    roc_auc=0.597 (+/-0.0144)
## 
## ── Iteration 22 ────────────────────────────────────────────────────────────────
## 
## i Current best:      roc_auc=0.7078 (@iter 20)
## i Gaussian process model
## ✓ Gaussian process model
## i Generating 5000 candidates
## i Predicted candidates
## i cost=0.0942, rbf_sigma=1.08e-07
## i Estimating performance
## ✓ Estimating performance
## ⓧ Newest results:    roc_auc=0.5864 (+/-0.0209)
## 
## ── Iteration 23 ────────────────────────────────────────────────────────────────
## 
## i Current best:      roc_auc=0.7078 (@iter 20)
## i Gaussian process model
## ✓ Gaussian process model
## i Generating 5000 candidates
## i Predicted candidates
## i cost=0.00983, rbf_sigma=0.00402
## i Estimating performance
## ✓ Estimating performance
## ⓧ Newest results:    roc_auc=0.5978 (+/-0.0139)
## 
## ── Iteration 24 ────────────────────────────────────────────────────────────────
## 
## i Current best:      roc_auc=0.7078 (@iter 20)
## i Gaussian process model
## ✓ Gaussian process model
## i Generating 5000 candidates
## i Predicted candidates
## i cost=31.6, rbf_sigma=6.53e-06
## i Estimating performance
## ✓ Estimating performance
## ⓧ Newest results:    roc_auc=0.5999 (+/-0.014)
## 
## ── Iteration 25 ────────────────────────────────────────────────────────────────
## 
## i Current best:      roc_auc=0.7078 (@iter 20)
## i Gaussian process model
## ✓ Gaussian process model
## i Generating 5000 candidates
## i Predicted candidates
## i cost=0.163, rbf_sigma=1.5e-05
## i Estimating performance
## ✓ Estimating performance
## ⓧ Newest results:    roc_auc=0.5981 (+/-0.0131)
## 
## ── Iteration 26 ────────────────────────────────────────────────────────────────
## 
## i Current best:      roc_auc=0.7078 (@iter 20)
## i Gaussian process model
## ✓ Gaussian process model
## i Generating 5000 candidates
## i Predicted candidates
## i cost=2.2, rbf_sigma=0.0941
## i Estimating performance
## ✓ Estimating performance
## ⓧ Newest results:    roc_auc=0.616 (+/-0.00825)
## 
## ── Iteration 27 ────────────────────────────────────────────────────────────────
## 
## i Current best:      roc_auc=0.7078 (@iter 20)
## i Gaussian process model
## ✓ Gaussian process model
## i Generating 5000 candidates
## i Predicted candidates
## i cost=0.00105, rbf_sigma=0.00144
## i Estimating performance
## ✓ Estimating performance
## ⓧ Newest results:    roc_auc=0.5974 (+/-0.0138)
show_best(svm_bo)
## Warning: No value of `metric` was given; metric 'roc_auc' will be used.
svm_bo %>% collect_metrics()
LS0tCnRpdGxlOiAiQXBwbHlpbmcgTWFjaGluZSBMZWFybmluZyB0byBCaXRjb2luIFByaWNlIFByZWRpY3Rpb24iCmF1dGhvcjogIlRzaGVyaW5nIFdhbmdjaHVrICYgQWFyb24gU2Fsb3QiCmRhdGU6IDEyLzA4LzIwMjEKb3V0cHV0OiAKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGRmX3ByaW50OiBwYWdlZAogICAgY29kZV9kb3dubG9hZDogdHJ1ZQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmBgYAoKCgpgYGB7cn0KbGlicmFyeShyZWFkeGwpCmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkodGlkeXZlcnNlKSAgICAgICAgICMgZm9yIGdyYXBoaW5nIGFuZCBkYXRhIGNsZWFuaW5nCmxpYnJhcnkodGlkeW1vZGVscykgICAgICAgICMgZm9yIG1vZGVsaW5nCmxpYnJhcnkodGhlbWlzKSAgICAgICAgICAgICMgZm9yIHN0ZXAgZnVuY3Rpb25zIGZvciB1bmJhbGFuY2VkIGRhdGEKbGlicmFyeShkb1BhcmFsbGVsKSAgICAgICAgIyBmb3IgcGFyYWxsZWwgcHJvY2Vzc2luZwpsaWJyYXJ5KHN0YWNrcykgICAgICAgICAgICAjIGZvciBzdGFja2luZyBtb2RlbHMKbGlicmFyeShuYW5pYXIpICAgICAgICAgICAgIyBmb3IgZXhhbWluaW5nIG1pc3NpbmcgdmFsdWVzIChOQXMpCmxpYnJhcnkobHVicmlkYXRlKSAgICAgICAgICMgZm9yIGRhdGUgbWFuaXB1bGF0aW9uCmxpYnJhcnkobW9kZXJuZGl2ZSkgICAgICAgICMgZm9yIEtpbmcgQ291bnR5IGhvdXNpbmcgZGF0YQpsaWJyYXJ5KHZpcCkgICAgICAgICAgICAgICAjIGZvciB2YXJpYWJsZSBpbXBvcnRhbmNlIHBsb3RzCmxpYnJhcnkocGF0Y2h3b3JrKSAgICAgICAgICMgZm9yIGNvbWJpbmluZyBwbG90cyBuaWNlbHkKbGlicmFyeShEQUxFWCkKbGlicmFyeShEQUxFWHRyYSkKbGlicmFyeShycGFydCkKbGlicmFyeShnZ3RoZW1lcykKdGhlbWVfc2V0KHRoZW1lX21pbmltYWwoKSkgIyBMaXNhJ3MgZmF2b3JpdGUgdGhlbWUKYGBgCgojIyMgSW50cm9kdWN0aW9uCgo8YnI+IEJpdGNvaW4sIGFuZCBvdGhlciBjcnlwdG9jdXJyZW5jaWVzLCBoYXZlIGJlZW4gcHJlc2VudCBmb3Igb3ZlciBhIGRlY2FkZSwgYW5kIGhhdmUgYmVjb21lIGluY3JlYXNpbmdseSBwcmV2YWxlbnQgaW4gdGhlIGdsb2JhbCBlY29ub21pYyBzeXN0ZW0uIEJpdGNvaW4sIHRoZSBmaXJzdCBhbmQgbW9zdCB3aWRlbHkgdXNlZCBjcnlwdG9jdXJyZW5jeSwgaXMgdXNlZCBhcyBhIHN0b3JlIG9mIHZhbHVlLCB0cmFkYWJsZSBhc3NldCwgYW5kIGEgZm9ybSBvZiBjdXJyZW5jeS4gVGhpcyBhbmFseXNpcyBhaW1zIHRvIHRha2UgYWR2YW50YWdlIG9mIHRoZSBvcGVuLXNvdXJjZSBibG9ja2NoYWluIG5ldHdvcmsgYmVoaW5kIEJpdGNvaW4sIGFuZCBvdXIgZ29hbCBpcyB0byB1bmRlcnN0YW5kIHRoZSB2YXJpb3VzIGZhY3RvcnMgdGhhdCBpbmZsdWVuY2UgdGhlIHByaWNlIG1vdmVtZW50IG9mIEJpdGNvaW4uIAoKCjxicj4gQml0Y29pbiBwb3B1bGFyaXplZCBibG9ja2NoYWluIHRlY2hub2xvZ3ksIGFuZCBTYXRvc2hpIE5ha2Ftb3RvIHVzZWQgaXQgdG8gc29sdmUgdGhlIEJ5emFuenRpbmUncyBHZW5lcmFsIFByb2JsZW0gYW5kIHRoZSBkb3VibGUtc3BlbmRpbmcgcHJvYmxlbSB3aXRob3V0IHRoZSBuZWVkIG9mIGEgY2VudHJhbCBhdXRob3JpdHkuIEl0IGlzIGFsc28gdGhlIGZhc3Rlc3QgZ3Jvd2luZyBhc3NldCBjbGFzcyB0byBoYXZlIHJlYWNoZWQgY2xvc2UgdG8gJDItMyB0cmlsbGlvbiBpbiBtYXJrZXQgY2FwaXRhbGl6YXRpb24gaW4gb3ZlciBhIGRlY2FkZS4gR2l2ZW4gdGhlIHJlY2VudCB0ZWNobm9sb2dpY2FsIGFkb3B0aW9uIGFzIHNlZW4gd2l0aCBUZXNsYSwgTWljcm9zdHJhdGVneSAmIEVsIFNhbHZhZG9yJ3MgZ292ZXJubWVudCwgd2UgYXJlIHBhcnRpY3VsYXJseSBpbnRlcmVzdGVkIHRvIGV4YW1pbmUgdGhlIHByaWNlIG1vdmVtZW50IG9mIEJpdGNvaW4gYnkgbG9va2luZyBhdCBjZXJ0YWluIHZhcmlhYmxlcyB0aGF0IGFyZSBwdWJsaWNhbGx5IGFjY2Vzc2libGUgdGhyb3VnaCBCaXRjb2luJ3MgcHVibGljIGxlZGdlci4KCgojIyMgRGF0YSBTb3VyY2VzCgo8YnI+IFdlIHVzZWQgdGhyZWUgZGlmZmVyZW50IGRhdGEgc291cmNlczogQ29pbk1ldHJpY3MsIEdsYXNzbm9kZXMgJiBGUkVELiBXZSBjb2xsZWN0ZWQgYW4gb3ZlcmFsbCBvZiAxNjIgdmFyaWFibGVzIHdpdGggb3ZlciA0MDAwIG9ic2VydmF0aW9uczsgaG93ZXZlciwgYWZ0ZXIgYmFzaWMgZXhwbGFuYXRvcnkgYW5hbHlzaXMgYW5kIGNsZWFuaW5nLCB3ZSBlbmRlZCB1cCB3aXRoIDg4IHZhcmlhYmxlcyBhbmQgMiwxMDAgb2JzZXJ2YXRpb25zLiBDb2luTWV0cmljcyB3YXMgdGhlIGxhcmdlc3QgYnVsayBvZiBvdXIgZGF0YSwgd2hlcmUgd2UgdXNlZCB0aGVpciBwdWJsaWMgQVBJIHRvIGV4dHJhY3QgZGFpbHkgZGF0YSBmcm9tIDIwMTAgdG8gcHJlc2VudCAoTm92ZW1iZXIsIDIwMjEpLiBXZSBhbHNvIHNlY3VyZWQgYSBHbGFzc25vZGVzIG1lbWJlcnNoaXAgdGhhdCBnYXZlIGFjY2VzcyB0byBkYWlseSBjaGFuZ2VzIGluIHNlbGVjdCB2YXJpYWJsZXMgbm90IHByZXNlbnQgaW4gdGhlIENvaW5NZXRyaWNzIEFQSS4gVGFraW5nIGZyb20gdGhlIHByZXZpb3VzIGxpdGVyYXR1cmUgcmV2aWV3LCB3ZSBhbHNvIGRlY2lkZWQgdG8gaW5jb3Jwb3JhdGUgY2VydGFpbiB2YXJpYWJsZXMgdGhhdCBoYXZlIGJlZW4gZXhhbWluZWQgaW4gcHJldmlvdXMgcGFwZXJzIC0gQ1BJLCBHb2xkIFByaWNlLCBOYXNkYXEgJiBCaXRjb2luIEdvb2dsZSBTZWFyY2ggdHJlbmRzLiAKCgojIyMgTG9hZGluZyB0aGUgRGF0YQoKV2Ugc3RhcnRlZCB3aXRoIDExIHllYXJzIG9mIGRhdGEgd2l0aCA0MDAwKyBvYnNlcnZhdGlvbnMsIGFuZCBkZWNpZGVkIHRvIGN1dCBvdXIgZGF0YXNldCB0byB0aGUgbW9zdCByZWNlbnQgNS15ZWFycyBkdWUgdG8gZGF0YS1kaXNjcmVwYW5jeSBhbmQgc2luY2UgQml0Y29pbiB3YXMgaW4gaXRzIG5hc2NlbnQgc3RhZ2VzIGVhcmxpZXIuIAoKYGBge3J9CkFEU19Qcm9qZWN0XzExXzI5XzIwMjFfdjggPC0gcmVhZF9leGNlbCgiQURTX1Byb2plY3QgLSAxMS4yOS4yMDIxLnY4Lnhsc3giKQpgYGAKCiMjIyBEYXRhIFByZXByb2Nlc3NpbmcgCgogV2UgZmlsdGVyZWQgdGhyb3VnaCB0aGUgdmFyaWFibGVzIGFuZCByZW1vdmVkIHJlcGV0aXRpdmUgb25lcy4gRm9yIGV4YW1wbGUsIGNlcnRhaW4gdmFyaWFibGVzIHdlcmUgZGlzcGxheWVkIGluIFVTRCBhbmQgbmF0aXZlIHVuaXRzIC0gd2UgcmVtb3ZlZCB2YXJpYWJsZXMgaW4gbmF0aXZlIHVuaXRzIHRvIGVuc3VyZSBjb25zaXN0ZW5jeSBhbmQgc3RhbmRhcmRpemF0aW9uIHRocm91Z2hvdXQuIFdlIGFsc28gcmVtb3ZlZCB0aGF0IG92ZXJsYXAgLSBgTlZULCBhZGp1c3RlZCwgOTBkIE1BYCAmIGBOVlQsIGZyZWUgZmxvYXQsIDkwZCBNQWAgYXJlIGV4YW1wbGVzIG9mIHR3byB0aGF0IHdlIHJlbW92ZWQgdG8gcHJldmVudCBvdmVyIGZpdHRpbmcuIAoKYGBge3J9Ck1hc3RlciA8LSBBRFNfUHJvamVjdF8xMV8yOV8yMDIxX3Y4ICU+JQpzZWxlY3QoLWMoYExhc3QgQ2FsY3VsYXRlZCBFT0QgbWV0cmljcyAodGltZSwgaW4gc2Vjb25kcylgLCBgQmxvY2sgQ291bnRgLCBgQmxvY2sgU2l6ZSAobWVhbiwgaW4gYnl0ZXMpYCwgYEJsb2NrIFdlaWdodCwgKG1lYW5gLCBgQ2FwaXRhbGl6YXRpb24gb2YgY3VycmVudCBzdXBwbHksIE1WUlZgLCBgQ2FwaXRhbGl6YXRpb24gb2YgZnJlZSBmbG9hdCwgTVZSVmAsIGBEaWZmaWN1bHR5LCBMYXN0IEJsb2NrYCwgYERpZmZpY3VsdHksIE1lYW4gQmxvY2tgLCBgTWVhbiBUcmFuc2FjdGlvbiBGZWUgcGVyIEJ5dGUgb2YgYWxsIGJsb2NrcywgbmF0aXZlIHVuaXRzYCwgYE1lYW4gVHJhbnNhY3Rpb24gRmVlIHBlciBCeXRlLCBuYXRpdmUgdW5pdHNgLCBgTWVkaWFuIFRyYW5zYWN0aW9uIEZlZSwgbmF0aXZlIHVuaXRzYCwgYE1lZGlhbiBGZWUgcGVyIFRyYW5zYWNhdGlvbiwgVVNEYCwgYEFkZHJlc3NlcyBiYWxhbmNlIGdyZWF0ZXIgdGhhbiAwLjAwMSBuYXRpdmUgdW5pdHNgLCBgQWRkcmVzc2VzIGJhbGFuY2UgZ3JlYXRlciB0aGFuIDAuMDEgbmF0aXZlIHVuaXRzYCwgYEFkZHJlc3NlcyBiYWxhbmNlIGdyZWF0ZXIgdGhhbiAwLjEgbmF0aXZlIHVuaXRzYCxgQWRkcmVzc2VzIGJhbGFuY2UgZ3JlYXRlciB0aGFuIDEwMCBuYXRpdmUgdW5pdHNgLCBgQWRkcmVzc2VzIGJhbGFuY2UgZ3JlYXRlciB0aGFuIDEwMEsgbmF0aXZlIHVuaXRzYCwgYEFkZHJlc3NlcyBiYWxhbmNlIGdyZWF0ZXIgdGhhbiAxMCBuYXRpdmUgdW5pdHNgLCBgQWRkcmVzc2VzIGJhbGFuY2UgZ3JlYXRlciB0aGFuIDEwSyBuYXRpdmUgdW5pdHNgLCBgQWRkcmVzc2VzIGJhbGFuY2UgZ3JlYXRlciB0aGFuIDEgbmF0aXZlIHVuaXRzYCwgYEFkZHJlc3NlcyBiYWxhbmNlIGdyZWF0ZXIgdGhhbiAxSyBuYXRpdmUgdW5pdHNgLCBgQWRkcmVzc2VzIGJhbGFuY2UgZ3JlYXRlciB0aGFuIDFNIG5hdGl2ZSB1bml0c2AsIGBBZGRyZXNzZXMgYmFsYW5jZSBncmVhdGVyIHRoYW4gMWluMTAwS2AsIGBBZGRyZXNzZXMgYmFsYW5jZSBncmVhdGVyIHRoYW4gMWluMTAwTWAsIGBBZGRyZXNzZXMgYmFsYW5jZSBncmVhdGVyIHRoYW4gMWluMTBCYCwgYEFkZHJlc3NlcyBiYWxhbmNlIGdyZWF0ZXIgdGhhbiAxaW4xMEtgLCBgQWRkcmVzc2VzIGJhbGFuY2UgZ3JlYXRlciB0aGFuIDFpbjEwS2AsIGBBZGRyZXNzZXMgYmFsYW5jZSBncmVhdGVyIHRoYW4gMWluMTBNYCwgYEFkZHJlc3NlcyBiYWxhbmNlIGdyZWF0ZXIgdGhhbiAxaW4xQmAsIGBBZGRyZXNzZXMgYmFsYW5jZSBncmVhdGVyIHRoYW4gMWluMUtgLCBgQWRkcmVzc2VzYmFsYW5jZSBncmVhdGVyIHRoYW4gMWluMU1gLCBgVG90YWwgRmVlcywgbmF0aXZlIHVuaXRzYCwgYEZsb3cgdG8gZXhjaGFuZ2VzLCBuYXRpdmUgdW5pdHNgLCBgRmxvdyBvdXQgZXhjaGFuZ2VzLCBuYXRpdmUgdW5pdHNgLCBgR2FzIExpbWl0IEJsb2NrYCwgYEdhcyBMaW1pdCwgdHJhbnNhY3Rpb25gLCBgR2FzIFVzZWQsIHRyYW5zYWN0aW9uYCwgYEhhc2ggUmF0ZSBNZWFuLCAzMGRgLCBgSXNzdWFuY2UsIG5hdGl2ZSB1bml0c2AsIGBJc3N1YW5jZSwgcGVyY2VudCBhbm51YWxpemVkYCwgYElzc3VhbmNlLCAgcGVyY2VudCBkYWlseWAsIGBJc3N1YW5jZSBUb3RhbCwgbmF0aXZlIHVuaXRzYCxgTlZULCBhZGp1c3RlZCwgOTBkIE1BYCwgYE5WVCwgYWRqdXN0ZWQsIGZyZWUgZmxvYXQsICA5MGQgTUFgLCBgUk9JLCAxeSAoJSlgLCBgUmV2ZW51ZSwgcGVyIGhhc2ggdW5pdCAoTmF0aXZlIEN1cnJlbmN5KWAsIGBSZXZlbnVlIERhaWx5LCBwZXIgaGFzaCB1bml0L3NlY29uZCAoTmF0aXZlIEN1cnJlbmN5KWAsIGBNaW5lciBSZXYgKE5hdGl2ZSBDdXJyZW5jeSlgLCBgU3VwcGx5LCAxMHkgQWN0aXZlYCwgYFN1cHBseSwgNXlyIEFjdGl2ZWAsIGBTdXBwbHksIDR5ciBBY3RpdmVgLCBgU3VwcGx5LCAzeXIgQWN0aXZlYCwgYFN1cHBseSwgMXlyIEFjdGl2ZSBUcmFpbGluZyAoJSlgLCBgU3VwcGx5LCBpbiBhZGRyZXNzZXMgKDwwLjAwMSwgbmF0aXZlIGN1cnJlbmN5KWAsIGBTdXBwbHksIGluIGFkZHJlc3NlcyAoPDAuMDFNLCBuYXRpdmUgY3VycmVuY3kpYCwgYFN1cHBseSwgaW4gYWRkcmVzc2VzICg8MC4xLCBuYXRpdmUgY3VycmVuY3kpYCwgYFN1cHBseSwgaW4gYWRkcmVzc2VzICg8MSwgbmF0aXZlIGN1cnJlbmN5KWAsIGBTdXBwbHksIGluIGFkZHJlc3NlcyAoPDEwLCBuYXRpdmUgY3VycmVuY3kpYCwgYFN1cHBseSwgaW4gYWRkcmVzc2VzICg8MTAwLCBuYXRpdmUgY3VycmVuY3kpYCwgYFN1cHBseSwgaW4gYWRkcmVzc2VzICg8MTAwSywgbmF0aXZlIGN1cnJlbmN5KWAsIGBTdXBwbHksIGluIGFkZHJlc3NlcyAoPDEwSywgbmF0aXZlIGN1cnJlbmN5KWAsIGBTdXBwbHksIGluIGFkZHJlc3NlcyAoPDFLLCBuYXRpdmUgY3VycmVuY3kpYCwgYFN1cHBseSwgaW4gYWRkcmVzc2VzICg8MU0sIG5hdGl2ZSBjdXJyZW5jeSlgLCBgU3VwcGx5LCBNaW5lciBhZGRyZXNzIHdpdGggMSBtaW5pbmcgZW50aXR5IChuYXRpdmUgY3VycmVuY3kpYCwgYFN1cHBseSwgQWxsIE1pbmVycyAobmF0aXZlIGN1cnJlbmN5KWAsIGBUcmFuc2FjdGlvbnMsIGFkanVzdGVkIHRyYW5zZmVyIHZhbHVlIChuYXRpdmUgY3VycmVuY3kpYCwgYFRyYW5zYWN0aW9ucywgbWVhbiB0cmFuc2ZlciB2YWx1ZSAobmF0aXZlIGN1cnJlbmN5KWAsIGBUcmFuc2FjdGlvbnMsIG1lZGlhbiB0cmFuc2ZlciB2YWx1ZSAobmF0aXZlIGN1cnJlbmN5KWAsIGBUcmFuc2FjdGlvbnMsIG1lYW4gdHJhbnNmZXIgdmFsdWUgKCQpYCwgYFRyYW5zYWN0aW9ucywgbWVkaWFuIHRyYW5zZmVyIHZhbHVlIChuYXRpdmUgY3VycmVuY3kpYCwgYFZlbG9jaXR5LCAxeXIgY3VycmVudCBzdXBwbHlgLCBgVm9sYXRpbGl0eSwgMTgwZCBkYWlseSByZXR1cm5zYCwgYFZvbGF0aWxpdHksIDMwZCBkYWlseSByZXR1cm5zYCwgYFByaWNlLCBCVENgLCBgUHJpY2UsICgkKWAsIGBUcmFuc2FjdGlvbnMsIG1lZGlhbiB0cmFuc2ZlciB2YWx1ZSAoJClgKSkKYGBgCgpXZSBhcnJpdmVkIGF0IDg4IHZhcmlhYmxlcyBhbmQgMjE0NSBvYnNlcnZhdGlvbnMuIFdlIHRoZW4gcGxvdHRlZCBvdmVyIDYwIGdyYXBocyBhbmQgcmVtb3ZlZCBjZXJ0YWluIHZhcmlhYmxlcyB0aGF0IGVpdGhlciBkaWQgbm90IGhhdmUgYW55IGNvcnJlbGF0aW9uIHRvIHRoZSBvdXRwdXQgb3Igd2hlcmUgb3RoZXIgdmFyaWFibGVzIGhhZCBhIHZpc3VhbGx5IG92ZXJsYXBwaW5nIGVmZmVjdC4gQmVsb3cgYXJlIHBsb3RzIG9mIHNlbGVjdCB2YXJpYWJsZXMgdGhhdCB3ZSByZW1vdmVkIGR1cmluZyB0aGlzIHByb2Nlc3MuIAoKYGBge3J9CmdncGxvdChNYXN0ZXIsCiAgICAgICBhZXMoeCA9IGBIYXNoIFJhdGUsIG1lYW5gLAogICAgICAgICAgIHkgPSBgUHJpY2VgKSkgKyAKICAgICAgICAgZ2VvbV9wb2ludCgpICsgCiAgICAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtLCBzZSA9IEZBTFNFKSArIHRoZW1lX2Vjb25vbWlzdCgpCgpnZ3Bsb3QoTWFzdGVyLAogICAgICAgYWVzKHggPSBgSXNzdWFuY2UsIGluIFVTRGAsCiAgICAgICAgICAgeSA9IGBQcmljZWApKSArIAogICAgICAgICBnZW9tX3BvaW50KCkgKyAKICAgICAgICAgZ2VvbV9zbW9vdGgobWV0aG9kID0gbG0sIHNlID0gRkFMU0UpICsgdGhlbWVfZWNvbm9taXN0KCkKCmdncGxvdChNYXN0ZXIsCiAgICAgICBhZXMoeCA9IGBJc3N1YW5jZSBUb3RhbCwgaW4gVVNEYCwKICAgICAgICAgICB5ID0gYFByaWNlYCkpICsgCiAgICAgICAgIGdlb21fcG9pbnQoKSArIAogICAgICAgICBnZW9tX3Ntb290aChtZXRob2QgPSBsbSwgc2UgPSBGQUxTRSkgKyB0aGVtZV9lY29ub21pc3QoKQoKCmdncGxvdChNYXN0ZXIsCiAgICAgICBhZXMoeCA9IGBTdXBwbHksIDMwRCBBY3RpdmVgLAogICAgICAgICAgIHkgPSBgUHJpY2VgKSkgKyAKICAgICAgICAgZ2VvbV9wb2ludCgpICsgCiAgICAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtLCBzZSA9IEZBTFNFKSArIHRoZW1lX2Vjb25vbWlzdCgpCgpnZ3Bsb3QoTWFzdGVyLAogICAgICAgYWVzKHggPSBgU3VwcGx5LCA3RCBBY3RpdmVgLAogICAgICAgICAgIHkgPSBgUHJpY2VgKSkgKyAKICAgICAgICAgZ2VvbV9wb2ludCgpICsgCiAgICAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtLCBzZSA9IEZBTFNFKSArIHRoZW1lX2Vjb25vbWlzdCgpCgpnZ3Bsb3QoTWFzdGVyLAogICAgICAgYWVzKHggPSBgU3VwcGx5LCA5MEQgQWN0aXZlYCwKICAgICAgICAgICB5ID0gYFByaWNlYCkpICsgCiAgICAgICAgIGdlb21fcG9pbnQoKSArIAogICAgICAgICBnZW9tX3Ntb290aChtZXRob2QgPSBsbSwgc2UgPSBGQUxTRSkrIHRoZW1lX2Vjb25vbWlzdCgpCmBgYAoKCjxicj4gQ29uc2VxdWVudGx5LCB0aGUgZ3JhcGhpY2FsIGV4cGxhbmRhdG9yeSBkYXRhIGFuYWx5c2lzIHNob3dlZCBjZXJ0YWluIHZhcmlhYmxlcyB0aGF0IGRlbW9uc3RyYXRlZCBhIHN0cm9uZyBjb3JlbGF0aW9uIHRvIHRoZSBvdXRwdXQuIFNvbWUgb2YgdGhlc2UgdmFyaWFibGVzIGNhbiBiZSBzZWVuIGJlbG93LiAKCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQpnZ3Bsb3QoTWFzdGVyLAogICAgICAgYWVzKHggPSBgR2FzIExpbWl0IEJsb2NrLCBtZWFuYCwKICAgICAgICAgICB5ID0gYFByaWNlYCkpICsgCiAgICAgICAgIGdlb21fcG9pbnQoKSArIAogICAgICAgICBnZW9tX3Ntb290aChtZXRob2QgPSBsbSwgc2UgPSBGQUxTRSkgKyB0aGVtZV9lY29ub21pc3QoKQoKZ2dwbG90KE1hc3RlciwKICAgICAgIGFlcyh4ID0gYE5WVCwgYWRqdXN0ZWRgLAogICAgICAgICAgIHkgPSBgUHJpY2VgKSkgKyAKICAgICAgICAgZ2VvbV9wb2ludCgpICsgCiAgICAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtLCBzZSA9IEZBTFNFKSArCiAgIHRoZW1lX2Vjb25vbWlzdCgpCgpnZ3Bsb3QoTWFzdGVyLAogICAgICAgYWVzKHggPSBgU3VwcGx5LCBpbiBhZGRyZXNzZXMgKDwkMTBNKWAsCiAgICAgICAgICAgeSA9IGBQcmljZWApKSArIAogICAgICAgICBnZW9tX3BvaW50KCkgKyAKICAgICAgICAgZ2VvbV9zbW9vdGgobWV0aG9kID0gbG0sIHNlID0gRkFMU0UpICsKICAgdGhlbWVfZWNvbm9taXN0KCkKYGBgCgpUaGUgY29kZSBiZWxvdyBzaG93cyB0aGUgdmFyaWFibGVzIHdlIHJlbW92ZWQgYWZ0ZXIgcGxvdHRpbmcgbW9zdCBvZiB0aGUgZ3JhcGhzIGFzIHNlZW4gYWJvdmUuIFRoaXMgZnVydGhlciBkcm9wcGVkIG91ciB2YXJpYWJsZSBjb3VudCBmcm9tIDg4IHZhcmlhYmxlcyB0byAyNy4gCgpgYGB7cn0KTWFzdGVyIDwtIE1hc3RlciAlPiUgc2VsZWN0KC1jKGBHYXMgbGltaXQgTWVhbiwgdHJhbnNhY3Rpb25gLCBgSGFzaCBSYXRlLCBtZWFuYCwgYElzc3VhbmNlLCBpbiBVU0RgLCBgSXNzdWFuY2UgVG90YWwsIGluIFVTRGAsIGBST0ksIDMwRCAoJSlgLCBgUmV2ZW51ZSBEYWlseSwgcGVyIGhhc2ggdW5pdC9zZWNvbmQgKFVTRClgLCBgUmV2ZW51ZSwgcGVyIGhhc2ggdW5pdCAoJClgLCBgU3VwcGx5IEVxdWFsaXR5IFJhdGlvYCwgYFN1cHBseSwgMTgwRCBBY3RpdmVgLCBgU3VwcGx5LCAxeXIgQWN0aXZlYCwgYFN1cHBseSwgMnkgQWN0aXZlYCwgYFN1cHBseSwgMXlyIEFjdGl2ZWAsIGBTdXBwbHksIE1pbmVyIGFkZHJlc3Mgd2l0aCAxIG1pbmluZyBlbnRpdHkgKCQpYCwgYFRyYW5zYWN0aW9ucywgY291bnRgLGBUcmFuc2FjdGlvbnMsIGNvdW50IChwZXIgc2Vjb25kKWAsIGBUcmFuc2FjdGlvbnMsIGFkanVzdGVkIHRyYW5zZmVyIHZhbHVlICgkKWAsIGBDb2luX0RheXNfRGVzdHJveWVkYCwgYEJhbGFuY2VfRXhjaGFuZ2VzXyglKWAsIGBUb3RhbF9FeGNoYW5nZV9JbmZsb3dfVm9sdW1lYCwgYFN1cHBseSwgMzBEIEFjdGl2ZWAsIGBTdXBwbHksIDdEIEFjdGl2ZWAsIGBTdXBwbHksIDkwRCBBY3RpdmVgLCBgU3VwcGx5LCBhZGRyZXNzIGJhbGFuY2UgKDwxaW4xMEspYCwgYFN1cHBseSwgYWRkcmVzcyBiYWxhbmNlICg8MWluMTBNKWAsIGBTdXBwbHksIGFkZHJlc3MgYmFsYW5jZSAoPDFpbjEwQilgLCBgU3VwcGx5LCBhZGRyZXNzIGJhbGFuY2UgKDwxaW4xMEspYCwgYFN1cHBseSwgYWRkcmVzcyBiYWxhbmNlICg8MWluMUIpYCwgYFN1cHBseSwgYWRkcmVzcyBiYWxhbmNlICg8MWluMUspYCwgYFN1cHBseSwgYWRkcmVzcyBiYWxhbmNlICg8MWluMU0pYCwgYFN1cHBseSwgaW4gYWRkcmVzc2VzICg8JDEpYCwgYFN1cHBseSwgaW4gYWRkcmVzc2VzICg8JDEwKWAsIGBTdXBwbHksIGluIGFkZHJlc3NlcyAoPCQxMDApYCwgYFN1cHBseSwgaW4gYWRkcmVzc2VzICg8JDEwMEspYCwgYFN1cHBseSwgaW4gYWRkcmVzc2VzICg8JDEwSylgLCBgU3VwcGx5LCBpbiBhZGRyZXNzZXMgKDwkMTBNKWAsIGBTdXBwbHksIGluIGFkZHJlc3NlcyAoPCQxSylgLCBgU3VwcGx5LCBoZWxkIGJ5IHRvcCAxJSBhZGRyZXNzZXNgLCBgU3VwcGx5LCAxMHlyIGV4cGVjdGVkIHN1cHBseWAsIGBTdXBwbHksIGZyZWUgZmxvYXRgLCBgU3VwcGx5LCBBbGwgTWluZXJzICgkKWAsIGBTdXBwbHksIE1pbmVyIGFkZHJlc3Mgd2l0aCAxIG1pbmluZyBlbnRpdHkgKCQpYCwgYFRyYW5zYWN0aW9ucywgY291bnRgLCBgVHJhbnNhY3Rpb25zLCBhZGp1c3RlZCB0cmFuc2ZlciB2YWx1ZSAoJClgLCBgQ29pbl9EYXlzX0Rlc3Ryb3llZGAsIGBDUElfKCUpYCkpCmBgYAoKYGBge3J9Ck1hc3RlciA8LSBNYXN0ZXIgJT4lIHNlbGVjdCgtYyhgQWxsIEFkZHJlc3NlcyBiYWxhbmNlYCwgYEFkZHJlc3NlcyBiYWxhbmNlIGdyZWF0ZXIgdGhhbiAkMTAwYCwgYEFkZHJlc3NlcyBiYWxhbmNlIGdyZWF0ZXIgdGhhbiAkMTBgLCBgQWRkcmVzc2VzIGJhbGFuY2UgZ3JlYXRlciB0aGFuICQxYCwgYENhcGl0YWxpemF0aW9uIG9mIGFjdGl2ZSBuYXRpdmUgdW5pdHMgc3VwcGx5LCBpbiBVU0RgLCBgQ2FwaXRhbGl6YXRpb24gb2YgZnJlZSBmbG9hdCwgaW4gVVNEYCwgYENhcGl0YWxpemF0aW9uIHJlYWxpemVkLCBVU0RgLCBgTlZULCBhZGp1c3RlZCwgZnJlZSBmbG9hdGAsIGBNaW5lciBSZXYgKCQpYCwgYFN1cHBseSwgY3VycmVudGAsIGBGbG93IHRyYW5zZmVycyBleGNoYW5nZXNgLCBgU3VwcGx5LCBhZGRyZXNzIGJhbGFuY2UgKDwxaW4xMDBNKWAsIGBTdXBwbHksIGluIGFkZHJlc3NlcyAoPCQxTSlgLCBgVHJhbnNhY3Rpb25zLCB0cmFuc2ZlciBjb3VudGAsIGBOVVBMYCwgYHJIT0RMYCwgYEFkZHJlc3NlcyBBY3RpdmUgQ291bnRgLGBBZGRyZXNzZXMgYmFsYW5jZSBncmVhdGVyIHRoYW4gJDEwS2AsIGBBZGRyZXNzZXMgYmFsYW5jZSBncmVhdGVyIHRoYW4gJDEwMEtgLCBgQWRkcmVzc2VzIGJhbGFuY2UgZ3JlYXRlciB0aGFuICQxS2AsIGBBZGRyZXNzZXMgYmFsYW5jZSBncmVhdGVyIHRoYW4gJDFNYCwgYEJsb2NrLCB3ZWlnaHQsIHRvdGFsYCkpCmBgYAoKCkluIHRoZSBzZWN0aW9uIGJlbG93LCB3ZSBjYXJyeSBvdXQgZnVydGhlciBwcmUtcHJvY2Vzc2luZy4gV2Ugc3RhcnQgYnkgY29udmVydGluZyB0aGUgRGF0ZSBpbnRvIG51bWVyaWMgZm9ybWF0IGZyb20gY2hhcmFjdGVyLCBzbyB0aGlzIHdpbGwgbm90IGNhdXNlIHByb2JsZW1zIHdoZW4gYnVpbGRpbmcgdGhlIHJlY2lwZXMgZm9yIHRoZSBtb2RlbHMgbW92aW5nIGZvcndhcmQuIFdlIGFsc28gaW50cm9kdWNlIGEgZmV3IG5ldyB2YXJpYWJsZXMgd2hpY2ggcmVwcmVzZW50IHRoZSBsYWdnZWQgY2hhcmFjdGVyaXN0aWNzIG9mIHRoZSBQcmljZXMgc3VjaCBhcyBgbGFnMWRheWAgd2hpY2ggZXNzZW50aWFsbHkgZW5hYmxlcyBlYWNoIG9ic2VydmF0aW9uIHRvIG9idGFpbiBtb3JlIHRoYW4gMSBwcmljZSB2YXJpYWJsZTsgaGVuY2UgdGhlIGBQcmljZWAgdmFyaWFibGUgd2lsbCByZXByZXNlbnQgdGhhdCBzcGVjaWZpYyBkYXlzIHByaWNlIGFuZCBgbGFnMWRheWAgd2lsbCByZXByZXNlbnQgdGhlIHByZXZpb3VzIGRheSdzIHByaWNlLgoKYGBge3J9CiBNYXN0ZXIgPC0gTWFzdGVyICU+JQogICAgICAgICAgIG11dGF0ZShEYXRlID0geW1kKERhdGUpKSAlPiUgCiAgICAgICAgICAgbXV0YXRlKERhdGVfbnVtID0gYXMubnVtZXJpYyhEYXRlKSkgJT4lCiAgICAgICAgICAgbXV0YXRlKGxhZzFkYXkgPSBsYWcoUHJpY2UsIDEsIG9yZGVyX2J5ID0gRGF0ZSksCiAgICAgICAgICAgICAgICAgIGxhZzdkYXkgPSBsYWcoUHJpY2UsIDcsIG9yZGVyX2J5ID0gRGF0ZSkpICU+JQogICAgICAgICAgIHJlcGxhY2VfbmEobGlzdChsYWcxZGF5ID0gMCwgbGFnN2RheSA9IDApKSAlPiUgCiAgICAgICAgICAgbXV0YXRlKERhaWx5X2F2ZyA9IChQcmljZSArIGxhZzFkYXkpLzIsCiAgICAgICAgICAgICAgICAgIFdlZWtseV9hdmcgPSAoUHJpY2UgKyBsYWc3ZGF5KS83KSAlPiUKICAgICAgICAgICBzZWxlY3QoLWMoQ1BJLCBgQlRDKCUpYCwgYE5EUSglKWAsIGBHb2xkKCUpYCwgQlRDX1NlYXJjaGVzLCBgQlRDUyglKWApKQpgYGAKCioqKiAKCiMjIyBNYWNoaW5lIExlYXJuaW5nIE1vZGVscyAKCldlIG1ha2UgdXNlIG9mIHRocmVlIGNvcmUgbW9kZWxzIGluIG91ciBhbmFseXNpcyAtLS0gCiAgICAgICAgICAgICAgKyBMYXNzbyBDbGFzc2lmaWNhdGlvbgogICAgICAgICAgICAgICsgUmFuZG9tIEZvcmVzdAogICAgICAgICAgICAgICsgRGVjaXNpb24gVHJlZXMKCldlIGFsc28gY2Fycnkgb3V0IGZ1cnRoZXIgZXhwbG9yYXRvcnkgYW5hbHlzaXMgd2l0aCBTdXBwb3J0IFZlY3RvciBNYWNoaW5lcyB0byBzZWUgaG93IGl0IHBlcmZvcm1zCgojIyMjIExhc3NvIC0gQmluYXJ5IENsYXNzaWZpY2F0aW9uIAoKSW5pdGlhbGx5LCB3ZSB3YW50ZWQgdG8gdXNlIGEgbGFzc28gcHJlZGljdGl2ZSByZWdyZXNzaW9uIHdoaWNoIHdvdWxkIG1vZGVsIGEgcXVhbnRpdGF0aXZlICpZKiB2YXJpYWJsZSAoYFByaWNlYCkuIFVwb24gcHJlbGltaW5hcnkgbW9kZWwgYnVpbGRpbmcgYW5kIHRlc3RpbmcsIHdlIG9ic2VydmVkIHRoYXQgdGhlIG1vZGVsIHBlcmZvcm1lZCB2ZXJ5IHBvb3JseSB3aXRoIHRoaXMgYXBwcm9hY2guIFRoaXMgd2FzIGxpa2VseSBkdWUgdG8gQml0Y29pbiBoYXZpbmcgZXh0cmVtZWx5IHZvbGF0aWxlIHByaWNlcyBjcmVhdGluZyBpbmNyZWFzZWQgdmFyaWF0aW9uIGFuZCBub2lzZSB3aGljaCBtYWRlIG1vZGVsbGluZyBpdCBhY2N1cmF0ZWx5IGRpZmZpY3VsdC4gSGVuY2UsIHdlIG1vdmVkIGZvcndhcmQgd2l0aCBhIGJpbmFyeSBsYXNzbyBjbGFzc2lmaWNhdGlvbi4KCkluIGJ1aWxkaW5nIHRoZSBMYXNzbywgd2UgY3JlYXRlZCBhIG5ldyB2YXJpYWJsZSBjYWxsZWQgYFByaWNlRGlyZWN0aW9uYCB3aGljaCB3b3VsZCBhbGxvdyB1cyB0byBsb29rIGF0IHByaWNlIGFjdGlvbnMgaW4gYSBiaW5hcnkgc3RydWN0dXJlIHdpdGggdGhlIGhlbHAgb2YgdGhlIGxhZ2dlZCB2YXJpYWJsZXMuIFdlIGRlZmluZSB0aGUgbWFpbiBkZXBlbmRlbnQgdmFyaWFibGUgYnkgcGFzc2luZyBvbiBhIGxvZ2ljYWwgY29uZGl0aW9uIHdoZXJlYnkgaWYgYFByaWNlYCBpcyBncmVhdGVyIHRoYW4gYGxhZzFkYXlgLCB0aGVuIHRoZSBjb25kaXRpb24gaXMgdHJ1ZSBhbmQgcmVwcmVzZW50cyBhIHByaWNlIGluY3JlYXNlICgxKS4gSWYgdGhlIGNvbmRpdGlvbiBmYWlscywgaXQgd2lsbCB0cmFuc2xhdGUgdG8gYSBwcmljZSBkZWNyZWFzZSAoMCkuIFRoaXMgaXMgc2hvd24gYmVsb3cuIAoKYGBge3J9Ck1hc3RlciA8LSBNYXN0ZXIgJT4lIG11dGF0ZShEYXRlID0geW1kKERhdGUpKSAlPiUgCiAgICAgICAgICAgbXV0YXRlKERhdGVfbnVtID0gYXMubnVtZXJpYyhEYXRlKSkgJT4lCiAgICAgICAgICAgbXV0YXRlKFByaWNlRGlyZWN0aW9uID0gaWZlbHNlKFByaWNlID4gbGFnMWRheSwgeWVzID0gMSwgbm8gPSAwKSkgJT4lCiAgICAgICAgICAgbXV0YXRlKFByaWNlRGlyZWN0aW9uID0gYXMuZmFjdG9yKFByaWNlRGlyZWN0aW9uKSkgCiAgICAgICAgCmBgYAoKCldlIHRha2UgYSBwcm9wb3J0aW9uYWwgc3BsaXQgb2YgMy80IGZvciB0aGUgc3BsaXQgYmV0d2VlbiBUcmFpbiBhbmQgVGVzdCwgd2hlcmVieSAwLjc1IGlzIGF0dHJpYnV0ZWQgdG8gdGhlIHRyYWluaW5nIHNldCBhbmQgMC4yNSBpcyB1c2VkIGZvciB0ZXN0aW5nLiBHaXZlbiB0aGUgcmFwaWQgYWNjZWxlcmF0aW9uIGluIHRoZSBwcmljZSBvZiBCaXRjb2luIG92ZXIgdGhlIHllYXJzLCB3ZSBkZWNpZGVkIG5vdCB0byB1c2UgYSB0aW1lIHNwbGl0LiAKCmBgYHtyfQpzZXQuc2VlZCg0NTYpCk1hc3Rlcl9TcGxpdCA8LSBpbml0aWFsX3NwbGl0KE1hc3RlciwgcHJvcCA9IDAuNzUpCgpNYXN0ZXJfVHJhaW5pbmcgPC0gdHJhaW5pbmcoTWFzdGVyX1NwbGl0KQpNYXN0ZXJfVGVzdGluZyA8LSB0ZXN0aW5nKE1hc3Rlcl9TcGxpdCkKYGBgCgoKSW4gdGhlIG5leHQgc3RlcCwgd2UgcHJlcGFyZSB0aGUgTGFzc28gbW9kZWwgcmVjaXBlLiBXZSByZW1vdmUgYERhdGVgIHNpbmNlIHRoZSByZWNpcGUgYnVpbGRpbmcgY2FuIG9ubHkgcHJvY2VzcyBudW1lcmljIGRhdGEgc3RydWN0dXJlcy4gV2UgYWxzbyBleGNsdWRlIGBOZXR3b3JrIERpc3RyaWJ1dGlvbiBGYWN0b3JgLCBgQ2FwaXRhbGl6YXRpb24gb2YgQ3VycmVudCBTdXBwbHksIGluIFVTRGAsIGFuZCBgU3VwcGx5LCBoZWxkIGJ5IHRvcCAxMCUgYWRkcmVzc2VzYCBhcyB0aGVzZSB2YXJpYWJsZXMgZGVtb25zdHJhdGVkIGlzc3VlcyBvZiBFbmRvZ2VuZWl0eS4gV2UgYWxzbyBleGNsdWRlIG90aGVyIHZhcmlhYmxlcyB3aGljaCBhcmUgbm90IHVzZWQgZm9yIHRoaXMgc2VjdGlvbi4KCmBgYHtyfQpsYXNzb19yZWNpcGUgPC0gcmVjaXBlKFByaWNlRGlyZWN0aW9uIH4gLiwKICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gTWFzdGVyX1RyYWluaW5nKSAlPiUKICAgICAgICAgICAgICAgIHN0ZXBfcm0oRGF0ZSwgV2Vla2x5X2F2ZywgRGFpbHlfYXZnLCBsYWc3ZGF5LCBsYWcxZGF5LCBgTmV0d29yayBkaXN0cmlidXRpb24gZmFjdG9yYCwgYENhcGl0YWxpemF0aW9uIG9mIEN1cnJlbnQgU3VwcGx5LCBpbiBVU0RgLCBgU3VwcGx5LCBoZWxkIGJ5IHRvcCAxMCUgYWRkcmVzc2VzYCkgJT4lCiAgICAgICAgICAgICAgICBzdGVwX211dGF0ZV9hdChhbGxfbnVtZXJpYygpLCAtYWxsX291dGNvbWVzKCksIGZuPSB+IGFzLm51bWVyaWMoLikpICU+JQogICAgICAgICAgICAgICAgc3RlcF9kdW1teShhbGxfbm9taW5hbCgpLCAtYWxsX291dGNvbWVzKCkpICU+JQogICAgICAgICAgICAgICAgdXBkYXRlX3JvbGUoRGF0ZV9udW0sIG5ld19yb2xlID0gIklEIikgJT4lCiAgICAgICAgICAgICAgICBzdGVwX3p2KGFsbF9wcmVkaWN0b3JzKCkpICU+JQogICAgICAgICAgICAgICAgc3RlcF9ub3JtYWxpemUoYWxsX3ByZWRpY3RvcnMoKSwgLWFsbF9ub21pbmFsKCkpCgpsYXNzb19yZWNpcGUgJT4lIHByZXAoKSAlPiUganVpY2UoKQpgYGAKCmBgYHtyfQpsYXNzb19tb2QgPC0gbG9naXN0aWNfcmVnKG1peHR1cmUgPSAxKSAlPiUKICAgICAgICAgICAgIHNldF9lbmdpbmUoImdsbW5ldCIpICU+JSAKICAgICAgICAgICAgIHNldF9hcmdzKHBlbmFsdHkgPSB0dW5lKCkpICU+JSAKICAgICAgICAgICAgIHNldF9tb2RlKCJjbGFzc2lmaWNhdGlvbiIpCgpsYXNzb193ZiA8LSB3b3JrZmxvdygpICU+JQogICAgICAgICAgICBhZGRfcmVjaXBlKGxhc3NvX3JlY2lwZSkgJT4lCiAgICAgICAgICAgIGFkZF9tb2RlbChsYXNzb19tb2QpIApgYGAKCmBgYHtyfQpzZXQuc2VlZCg0OTQpCmJ0Y19jdiA8LSB2Zm9sZF9jdihNYXN0ZXJfVHJhaW5pbmcsCiAgICAgICAgICAgICAgICAgICB2ID0gMTApCgpwZW5hbHR5X2dyaWQgPC0gZ3JpZF9yZWd1bGFyKHBlbmFsdHkoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSAxMCkKCmN0cmxfZ3JpZCA8LSBjb250cm9sX3N0YWNrX3Jlc2FtcGxlcygpCgptZXRyaWMgPC0gbWV0cmljX3NldChhY2N1cmFjeSkKYGBgCgpgYGB7cn0KbGFzc29fdHVuZSA8LSBsYXNzb193ZiAlPiUKICAgICAgICAgICAgICB0dW5lX2dyaWQocmVzYW1wbGVzID0gYnRjX2N2LAogICAgICAgICAgICAgICAgICAgICAgICBncmlkID0gcGVuYWx0eV9ncmlkLAogICAgICAgICAgICAgICAgICAgICAgICBjb250cm9sID0gY3RybF9ncmlkKQoKbGFzc29fdHVuZSAlPiUgY29sbGVjdF9tZXRyaWNzKCkKYGBgCgpgYGB7cn0KYmVzdF9wYXJhbSA8LSBsYXNzb190dW5lICU+JQogICAgICAgICAgICAgIHNlbGVjdF9iZXN0KG1ldHJpYyA9ICJhY2N1cmFjeSIpCmJlc3RfcGFyYW0KYGBgCgpgYGB7cn0KZmluYWxfbGFzc28gPC0gbGFzc29fd2YgJT4lIGZpbmFsaXplX3dvcmtmbG93KGJlc3RfcGFyYW0pICU+JQogICAgICAgICAgICAgICBmaXQoZGF0YSA9IE1hc3Rlcl9UcmFpbmluZykKCmZpbmFsX2xhc3NvICU+JSBwdWxsX3dvcmtmbG93X2ZpdCgpICU+JSB0aWR5KCkKYGBgCgpgYGB7cn0KbGlicmFyeSh2aXApCmZpbmFsX2xhc3NvICU+JSAKICAgICAgZml0KE1hc3Rlcl9UcmFpbmluZykgJT4lCiAgICAgIGV4dHJhY3RfZml0X3BhcnNuaXAoKSAlPiUKICAgICAgdmkobGFtZGEgPSBsb3dlc3Rfcm1zZSRwZW5hbHR5KSAlPiUKICAgICAgbXV0YXRlKEltcG9ydGFuY2UgPSBhYnMoSW1wb3J0YW5jZSksCiAgICAgICAgICAgICBWYXJpYWJsZSA9IGZjdF9yZW9yZGVyKFZhcmlhYmxlLCBJbXBvcnRhbmNlKSkgJT4lCiAgICAgZ2dwbG90KGFlcyh4ID0gSW1wb3J0YW5jZSwgeSA9IFZhcmlhYmxlLCBmaWxsID0gU2lnbikpICsKICAgICBnZW9tX2NvbCgpICsgCiAgICAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwwKSkgKwogICAgIGxhYnMoeSA9IE5VTEwpCmBgYAoKYGBge3J9Cmxhc3NvX2ZpdCA8LSBmaW5hbF9sYXNzbyAlPiUKICBsYXN0X2ZpdChNYXN0ZXJfU3BsaXQpCgpsYXNzb19maXQKCnRlc3RfcGVyZm9ybWFuY2VfbGFzc28gPC0gbGFzc29fZml0ICU+JSBjb2xsZWN0X21ldHJpY3MoKQp0ZXN0X3BlcmZvcm1hbmNlX2xhc3NvCmBgYAoKVGhlIExhc3NvIGNsYXNzaWZpY2F0aW9uIG1vZGVsIHNlbGVjdHMgdGhlIHZhcmlhYmxlcyBiYXNlZCBpbiB0aGUgbWFnbml0dWRlIG9mIHRoZWlyIGNvZWZmaWNpZW50cy4gV2UgYnVpbHQgdGhlIExhc3NvIGJ5IHVzaW5nIGEgcGVuYWx0eSBncmlkIGxldmVsIG9mIDEwLCBDViByZXNhbXBsaW5nIG9mIDEwIGZvbGRzLCBhbmQgbW92ZWQgZm9yd2FyZCBieSBzZWxlY3RpbmcgdGhlIHR1bmluZyBwYXJhbWV0ZXIgd2l0aCB0aGUgaGlnaGVzdCBwcmVkaWN0aW9uIGFjY3VyYWN5IGFzIHRoZSBwYXJhbWV0ZXIgZm9yIHRoZSBmaW5hbCBtb2RlbC4gQXMgc2hvd24gYWJvdmUsIHRoZSBwcmVkaWN0aW9uIGFjY3VyYWN5IGZvciB0aGUgbGFzc28gbW9kZWwgaXMgNTkuNCUsIHByZWRpY3RpbmcgdGhlIGNvcnJlY3QgcHJpY2UgbW92ZW1lbnQgNTkuNCUgb2YgdGhlIHRpbWUgd2l0aCBSZWNlaXZlciBvcGVyYXRpbmcgY2hhcmFjdGVyaXN0aWMgYXJlYSB1bmRlciBjdXJ2ZSBvZiA2MiUuICAgCgojIyMjIFJhbmRvbSBGb3Jlc3QgLSBDbGFzc2lmaWNhdGlvbgoKVGhlIG5leHQgbW9kZWwgd2UgdXNlIGlzIGEgUmFuZG9tIEZvcmVzdC4gU2ltaWxhciB0byB0aGUgbGFzc28gbW9kZWwsIHdlIHRha2UgbW9zdCBvZiB0aGUgc2FtZSBjb25jZXB0cyBhbmQgaWRlYXMgaW4gYnVpbGRpbmcgdGhlIG1vZGVsIHdpdGggYFByaWNlRGlyZWN0aW9uYCBiZWluZyB0aGUgbWFpbiBkZXBlbmRlbnQgdmFyaWFibGUuIFdlIGFkanVzdCBmb3IgdGhlIHJlbW92YWwgb2YgdGhlIHZhcmlhYmxlcyB3ZSBleGNsdWRlZCBpbiB0aGUgbGFzc28gZm9yIHRoZSBzYW1lIHJlYXNvbnMgYXMgYWJvdmUuIAoKV2UgYnVpbGQgb3VyIHJhbmRvbSBmb3Jlc3QgbW9kZWwgdXNpbmcgMTAgdHJlZXMgd2l0aCBwZW5hbHR5IGdyaWQgb2YgbGV2ZWwgMyB0byB0dW5lIHRoZSBmb3Jlc3QgYW5kIGEgcmVzYW1wbGluZyBDViBvZiAxMCBmb2xkcy4gVGhlIG1vZGVsIHJldHVybnMgdGhlIHRoZSBtb2RlIG9mIHRoZSBwcmVkaWN0aW9ucyBmb3IgdGhlIDEwIHRyZWVzLiAKCmBgYHtyfQpzZXQuc2VlZCg0NTYpCnJmX3JlY2lwZSA8LSByZWNpcGUoUHJpY2VEaXJlY3Rpb24gfi4sCiAgICAgICAgICAgICAgICAgICAgZGF0YSA9IE1hc3Rlcl9UcmFpbmluZykgJT4lCiAgICAgICAgICAgICBzdGVwX211dGF0ZV9hdChhbGxfbnVtZXJpYygpLCBmbiA9IH5hcy5udW1lcmljKC4pKSAlPiUKICAgICAgICAgICAgIHN0ZXBfcm0oV2Vla2x5X2F2ZywgRGFpbHlfYXZnLCBsYWc3ZGF5LCBsYWcxZGF5LCBgTmV0d29yayBkaXN0cmlidXRpb24gZmFjdG9yYCwgYENhcGl0YWxpemF0aW9uIG9mIEN1cnJlbnQgU3VwcGx5LCBpbiBVU0RgLCBgU3VwcGx5LCBoZWxkIGJ5IHRvcCAxMCUgYWRkcmVzc2VzYCkgCgpyZl9yZWNpcGUgJT4lIHByZXAoKSAlPiUganVpY2UoKQpgYGAKCmBgYHtyfQpyZl9tb2RlbCA8LSByYW5kX2ZvcmVzdChtdHJ5ID0gdHVuZSgpLAogICAgICAgICAgICAgICAgICAgICAgICBtaW5fbiA9IHR1bmUoKSwKICAgICAgICAgICAgICAgICAgICAgICAgdHJlZXMgPSAxMCkgJT4lCiAgICAgICAgICAgIHNldF9tb2RlKCJjbGFzc2lmaWNhdGlvbiIpICU+JQogICAgICAgICAgICBzZXRfZW5naW5lKCJyYW5nZXIiKQoKcmZfd29ya2Zsb3cgPC0gd29ya2Zsb3coKSAlPiUKICAgICAgICAgICAgICAgYWRkX3JlY2lwZShyZl9yZWNpcGUpICU+JQogICAgICAgICAgICAgICBhZGRfbW9kZWwocmZfbW9kZWwpCgpyZl9wZW5hbHR5X2dyaWQgPC0gZ3JpZF9yZWd1bGFyKGZpbmFsaXplKG10cnkoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBNYXN0ZXJfVHJhaW5pbmcgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KC1QcmljZURpcmVjdGlvbikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbl9uKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gMikKCnJmX3R1bmUgPC0gcmZfd29ya2Zsb3cgJT4lCiAgICAgICAgICAgdHVuZV9ncmlkKHJlc2FtcGxlcyA9IGJ0Y19jdiwKICAgICAgICAgICAgICAgICAgICAgZ3JpZCA9IHJmX3BlbmFsdHlfZ3JpZCwKICAgICAgICAgICAgICAgICAgICAgY29udHJvbCA9IGNvbnRyb2xfc3RhY2tfZ3JpZCgpKQoKcmZfdHVuZSAlPiUgY29sbGVjdF9tZXRyaWNzKCkKCmBgYAoKYGBge3J9CmJlc3RfcGFyYW1fcmYgPC0gcmZfdHVuZSAlPiUKICAgICAgICAgICAgICBzZWxlY3RfYmVzdChtZXRyaWMgPSAiYWNjdXJhY3kiKQpiZXN0X3BhcmFtX3JmCmBgYApgYGB7cn0KcmZfZmluYWwgPC0gcmZfd29ya2Zsb3cgJT4lIGZpbmFsaXplX3dvcmtmbG93KGJlc3RfcGFyYW1fcmYpCnJmX2ZpdCA8LSByZl9maW5hbCAlPiUKICBsYXN0X2ZpdChNYXN0ZXJfU3BsaXQpCgpyZl9maXQKCnRlc3RfcGVyZm9ybWFuY2VfcmYgPC0gcmZfZml0ICU+JSBjb2xsZWN0X21ldHJpY3MoKQp0ZXN0X3BlcmZvcm1hbmNlX3JmCmBgYAoKV2Ugb2JzZXJ2ZSB0aGF0IHRoZSByYW5kb20gZm9yZXN0IHBlcmZvcm1zIHBvb3JseSBjb21wYXJlZCB0byB0aGUgbGFzc28gbW9kZWwuIE91ciBiZXN0IHBlcmZvcm1pbmcgcmFuZG9tIGZvcmVzdCBtb2RlbCB3aXRoIHRoZSBiZXN0IHR1bmVkIHBhcmFtZXRlciBoYXMgYSBwcmVkaWN0aW9uIGFjY3VyYWN5IG9mIDUyLjMlIHdoZW4gdGVzdGVkIGFnYWluc3QgdGhlIHRlc3Qgc3BsaXQuIFRoZSBoaWdoZXN0IHByZWRpY3Rpb24gYWNjdXJhY3kgaXMgc2VlbiBmb3IgdHVuaW5nIHBhcmFtZXRlciBtdHJ5ID0gMjYgYW5kIG1pbl9uID0gMiB3aXRoIGEgcHJlZGljdGlvbiBhY2N1cmFjeSBvZiA1Ni4zJS4gCgojIyMjIERlY2lzaW9uIHRyZWUgCgpUaGUgbGFzdCBtb2RlbCB3ZSB1c2UgdG8gY3JlYXRlIGEgc3RhY2tlZCBtb2RlbCBpcyBhIGRlY2lzaW9uIHRyZWUuIFdlIHV0aWxpemUgdGhlIHNhbWUgcmVjaXBlIGJ1aWx0IGZvciB0aGUgcmFuZG9tIGZvcmVzdCB0byBidWlsZCB0aGUgdHJlZSBtb2RlbCB3aXRoIHRoZSBzYW1lIHBhcmFtZXRlcnMuIFRoZSByZXN1bHRzIHdlIG9idGFpbmVkIGJlbG93IHdlcmUgc3VwcmlzaW5nIGJlY2F1c2UgdGhlIHRyZWUgbW9kZWwgb3V0cGVyZm9ybXMgdGhlIHJhbmRvbSBmb3Jlc3QgbW9kZWwgd2l0aCBtb2RlbCBwcmVkaWN0aW9uIGFjY3VyYWN5IG9mIDU5LjglIGFuZCB0ZXN0IGFjY3VyYWN5IG9mIDYwJS4gVGhlIHRyZWUgbW9kZWwgZGVsaXZlcnMgdGhlIGhpZ2hlc3QgcHJlZGljdGlvbiBhY2N1cmFjeSBmb3IgdGVzdGluZyBhbW9uZyBhbGwgdGhlIHRocmVlIG1vZGVscy4gCgpgYGB7cn0KI2RlY2lzaW9uIHRyZWVzCnNldC5zZWVkKDQ1NikKCnRyZWVfbW9kZWwgPC0KICBkZWNpc2lvbl90cmVlKCkgJT4lCiAgc2V0X21vZGUoImNsYXNzaWZpY2F0aW9uIikgJT4lCiAgc2V0X2VuZ2luZSgicnBhcnQiKQoKdHJlZV93b3JrZmxvdyA8LQogIHdvcmtmbG93KCkgJT4lCiAgYWRkX3JlY2lwZShyZl9yZWNpcGUpICU+JSAgCiAgYWRkX21vZGVsKHRyZWVfbW9kZWwpCgp0cmVlX2ZpdCA8LQogIHRyZWVfd29ya2Zsb3cgJT4lCiAgZml0X3Jlc2FtcGxlcyhidGNfY3YsCiAgICAgICAgICAgICAgICBjb250cm9sID0gY29udHJvbF9zdGFja19yZXNhbXBsZXMoKQogICkKCmNvbGxlY3RfbWV0cmljcyh0cmVlX2ZpdCkKYGBgCgpgYGB7cn0KYmVzdF9wYXJhbV90cmVlIDwtIHRyZWVfZml0ICU+JQogICAgICAgICAgICAgIHNlbGVjdF9iZXN0KG1ldHJpYyA9ICJhY2N1cmFjeSIpCmJlc3RfcGFyYW1fdHJlZQpgYGAKCmBgYHtyfQp0cmVlX2ZpbmFsIDwtIHRyZWVfd29ya2Zsb3cgJT4lIGZpbmFsaXplX3dvcmtmbG93KGJlc3RfcGFyYW1fcmYpCgpUcmVlX2ZpdCA8LSB0cmVlX2ZpbmFsICU+JQogIGxhc3RfZml0KE1hc3Rlcl9TcGxpdCkKClRyZWVfZml0Cgp0ZXN0X3BlcmZvcm1hbmNlX3RyZWUgPC0gVHJlZV9maXQgJT4lIGNvbGxlY3RfbWV0cmljcygpCnRlc3RfcGVyZm9ybWFuY2VfdHJlZQpgYGAKCgpgYGB7ciwgZWNobz1GQUxTRX0KbGlicmFyeSh2aXApCnRyZWVfZmluYWwgJT4lIAogICAgICBmaXQoTWFzdGVyX1RyYWluaW5nKSAlPiUKICAgICAgZXh0cmFjdF9maXRfcGFyc25pcCgpICU+JQogICAgICB2aShhY2N1cmFjeSA9IGJlc3RfcGFyYW1fdHJlZSkgJT4lCiAgICAgIG11dGF0ZShJbXBvcnRhbmNlID0gYWJzKEltcG9ydGFuY2UpLAogICAgICAgICAgICAgVmFyaWFibGUgPSBmY3RfcmVvcmRlcihWYXJpYWJsZSwgSW1wb3J0YW5jZSkpICU+JQogICAgIGdncGxvdChhZXMoeCA9IEltcG9ydGFuY2UsIHkgPSBWYXJpYWJsZSkpICsKICAgICBnZW9tX2NvbCgpICsgCiAgICAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwwKSkgKwogICAgIGxhYnMoeSA9IE5VTEwpCmBgYAoKIyMjIyBTdGFja2luZyAKCmBgYHtyfQpsYXNzb190dW5lICU+JQogIGNvbGxlY3RfbWV0cmljcygpCmBgYAoKYGBge3J9CnJmX3R1bmUgJT4lCiAgY29sbGVjdF9tZXRyaWNzKCkKYGBgCgpgYGB7cn0KdHJlZV9maXQgJT4lCiAgY29sbGVjdF9tZXRyaWNzKCkKYGBgCgpgYGB7ciwgd2FybmluZz1GQUxTRX0KTW9kZWxzX3N0YWNrIDwtCiAgc3RhY2tzKCkgJT4lCiAgYWRkX2NhbmRpZGF0ZXMobGFzc29fdHVuZSkgJT4lCiAgYWRkX2NhbmRpZGF0ZXMocmZfdHVuZSkgJT4lCiAgYWRkX2NhbmRpZGF0ZXModHJlZV9maXQpCmBgYAoKYGBge3J9Ck1vZGVsc19ibGVuZCA8LQogIE1vZGVsc19zdGFjayAlPiUKICBibGVuZF9wcmVkaWN0aW9ucygpCk1vZGVsc19ibGVuZApgYGAKCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQpNb2RlbF9maW5hbF9zdGFjayA8LSBNb2RlbHNfYmxlbmQgJT4lCiAgZml0X21lbWJlcnMoKSAKYGBgCgojIyMgRGF0YSBWaXN1YWxpemF0aW9uCgpgYGB7cn0KTWFzdGVyICU+JSAKICBzZWxlY3QoYFByaWNlYCwgYERhdGVfbnVtYCwgYFNPUFJgLCBgQWRqdXN0ZWRfRG9ybWFuY3lfRmxvd2ApICU+JQogIHBpdm90X2xvbmdlcihjb2xzPS1gRGF0ZV9udW1gLCBuYW1lc190byA9ICJ2YXJpYWJsZSIsIHZhbHVlc190byA9ICJ2YWx1ZXMiKSAlPiUgCiAgZ2dwbG90KGFlcyhgRGF0ZV9udW1gLCBgdmFsdWVzYCkpICsgZ2VvbV9saW5lKCkgKyBmYWNldF93cmFwKHZhcnMoYHZhcmlhYmxlYCksIG5jb2wgPSAxLCBzY2FsZXMgPSAiZnJlZV95IikgKyB0aGVtZV93c2ooKQpgYGAKClRoaXMgcGxvdCBzaG93cyB0aGUgZG9ybWFuY3kgZmxvd3Mgb2YgZG9ybWFudCBjb2lucyB0aHJvdWdob3V0IGEgYml0Y29pbiBjeWNsZS4gRm9yIGV4YW1wbGUsIGF0IHRoZSBwZWFrIG9mIHRoZSBjeWNsZSBpbiAyMDE4IChEYXRlX251bSA8IDE3NTAwKSwgbW9zdCBvZiB0aGUgdGhlIGRvcm1hbnQgY29pbnMgd2VyZSBiZWluZyB0cmFzYWN0ZWQgYW5kIHRyYW5mZXJyZWQgYWNyb3NzIGNoYW5nZXMuIEl0IGNvbWVzIHRvIHNob3cgaG93IGluIHRoZSBjdXJyZW50IGN5Y2xlIHRoZSBkb3JtYW50IGNvaW5zIGhhdmUgbm90IHJlYWNoZWQgdGhhdCBsZXZlbCBvZiB0cmFuc2FjdGlvbnMgYW5kIG1vdmVtZW50LiBUaGUgU09QUiwgb24gdGhlIG90aGVyIGhhbmRzLCBoZWxwcyB1bmRlcnN0YW5kIHNob3J0LXRlcm0gcHJpY2UgbW92ZW1lbnQuIER1cmluZyBsYXJnZSBkdW1wcywgbGlrZSBNYXJjaCAyMDIwIChEYXRlX251bSA8IDE4NTAwKSwgd2Ugc2VlIGhvdyB0aGUgU09QUiBkcm9wcyBzaWduaWZpY2FudGx5IGFyb3VuZCAwLjksIHdoaWxlIGl0IHJlYWNoZXMgY2xvc2VyIHRvIDEuMTAgYXQgYnVsbCBtYXJrZXQgbG9jYWwgcGVha3MuIAoKCmBgYHtyfQpNYXN0ZXIgJT4lIAogIHNlbGVjdChgUHJpY2VgLCBgRGF0ZV9udW1gLCBgU3VwcGx5LCBoZWxkIGJ5IHRvcCAxMDAgYWRkcmVzc2VzYCwgYFN1cHBseSwgVG90YWwgQWN0aXZlYCkgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHM9LWBEYXRlX251bWAsIG5hbWVzX3RvID0gInZhcmlhYmxlIiwgdmFsdWVzX3RvID0gInZhbHVlcyIpICU+JSAKICBnZ3Bsb3QoYWVzKGBEYXRlX251bWAsIGB2YWx1ZXNgKSkgKyBnZW9tX2xpbmUoKSArIGZhY2V0X3dyYXAodmFycyhgdmFyaWFibGVgKSwgbmNvbCA9IDEsIHNjYWxlcyA9ICJmcmVlX3kiKSArIHRoZW1lX3dzaigpCmBgYAoKT25lIG9mIHRoZSBpbnRlcmVzdGluZyB2YXJpYWJsZXMgdG8gZXhhbWluZSBpcyBTdXBwbHksIGhlbGQgYnkgdGhlIHRvcCAxMDAgYWRkcmVzc2VzLiBUaGVzZSBhcmUgdGhlIGxhcmdlc3QgYml0Y29pbiBob2xkZXJzLCBhbmQgdGhlaXIgaG9sZGluZ3Mgb2Z0ZW4gZGljdGF0ZSB0aGUgcHJpY2UgZGlyZWN0aW9uIG9mIEJpdGNvaW4gLSB0aGlzIGlzIHByaW1hcmlseSBkdWUgdG8gdGhlIGZhY3QgdGhhdCB0aGV5IGhhdmUgZXhwZXJpZW5jZWQgbXVsdGlwbGUgY3ljbGVzLCBjYW4gYW50aWNpcGF0ZSBob3cgdGhlIGN1cnJlbnQgY3ljbGUgbWF5IHBsYXkgb3V0LCBhbmQgcG90ZW50aWFsbHkgYmVjYXVzZSB0aGV5IGhvbGQgZ3JlYXRlciBpbmZvcm1hdGlvbiBhYm91dCB1cGNvbWluZyBwcmljZSBjYXRhbHlzdHMuIFRoZW0gaW5jcmVhc2luZyB0aGVpciBob2xkaW5ncyBzaWduaWZpY2FudGx5IGR1cmluZyB0aGUgTWFyY2ggMjAyMCBkdW1wIGNvdWxkIGJlIGEgcG90ZW50aWFsIGluZGljYXRvciB0byBmcm9udHJ1biBhIGxhcmdlIGluY3JlYXNlIGluIHRoZSBwcmljZSBvZiBiaXRjb2luLiAKCmBgYHtyfQpNYXN0ZXIgJT4lIAogIHNlbGVjdChgUHJpY2VgLCBgRGF0ZV9udW1gLCBgQ29pbl9ZZWFyc19EZXN0cm95ZWRgLCBgTlZULCBhZGp1c3RlZGApICU+JQogIHBpdm90X2xvbmdlcihjb2xzPS1gRGF0ZV9udW1gLCBuYW1lc190byA9ICJ2YXJpYWJsZSIsIHZhbHVlc190byA9ICJ2YWx1ZXMiKSAlPiUgCiAgZ2dwbG90KGFlcyhgRGF0ZV9udW1gLCBgdmFsdWVzYCkpICsgZ2VvbV9saW5lKCkgKyBmYWNldF93cmFwKHZhcnMoYHZhcmlhYmxlYCksIG5jb2wgPSAxLCBzY2FsZXMgPSAiZnJlZV95IikgKyB0aGVtZV93c2ooKQpgYGAKClRoZSBOZXR3b3JrIFZhbHVlIHRvIFRyYW5zYWN0aW9uIGhlbHBzIHVuZGVyc3RhbmQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRyYW5zZmVyIHZvbHVtZSBhbmQgdGhlIG1hcmtldCBjYXAgb2YgQml0Y29pbi4gSW4gb3JkZXIgd29yZHMsIGEgaGlnaGVyIE5WVCBpbmRpY2F0ZXMgdGhhdCB0aGUgdmFsdWF0aW9uIG9mIHRoZSBCaXRjb2luIG5ldHdvcmsgaXMgZ3JlYXRlciB0aGFuIHRoZSB0cmFuc2FjdGlvbiB2b2x1bWUgKGEgbW9yZSBpbnRyaW5zaWMgdmFsdWUgaW5kaWNhdG9yKS4gVGhlIGJsb3ctb2ZmIHRvcCBpbiAyMDE4IGNvdWxkIGJlIGFudGljaXBhdGVkIGJ5IHRoZSBzdWRkZW4gaW5jcmVhc2UgaW4gTlZULCB3aGljaCB3b3VsZCBpbmRpY2F0ZSBhIGJ1YmJsZS1saWtlIGJlaGF2aW9yIGluIHRlcm1zIG9mIEJpdGNvaW4ncyB0cmFuc2FjdGlvbiB2b2x1bWUgdnMgaXQncyBtYXJrZXQgY2FwLiAKCkJlbG93IGFyZSBhZGRpdGlvbmFsIGdyYXBocyBwbG90dGVkIHdpdGggdGhlIHByaWNlIG9mIEJpdGNvaW4gb3ZlciB0aGUgc3BhbiBvZiA1IHllYXJzLiAKCmBgYHtyfQpNYXN0ZXIgJT4lIAogICAgc2VsZWN0KGBQcmljZWAsIGBEYXRlX251bWAsYE1pbmVyIFJldiwgVG90YWwgKCQpYCwgYFRvdGFsIEZlZXMsIFVTRGApICU+JQogIHBpdm90X2xvbmdlcihjb2xzPS1gRGF0ZV9udW1gLCBuYW1lc190byA9ICJ2YXJpYWJsZSIsIHZhbHVlc190byA9ICJ2YWx1ZXMiKSAlPiUgCiAgZ2dwbG90KGFlcyhgRGF0ZV9udW1gLCBgdmFsdWVzYCkpICsgZ2VvbV9saW5lKCkgKyBmYWNldF93cmFwKHZhcnMoYHZhcmlhYmxlYCksIG5jb2wgPSAxLCBzY2FsZXMgPSAiZnJlZV95IikgKyB0aGVtZV93c2ooKQpgYGAKCmBgYHtyfQpNYXN0ZXIgJT4lIAogIHNlbGVjdChgUHJpY2VgLCBgRGF0ZV9udW1gLCBgR2FzIExpbWl0IEJsb2NrLCBtZWFuYCwgYEFkZHJlc3NlcyBiYWxhbmNlIGdyZWF0ZXIgdGhhbiAkMTBNYCkgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHM9LWBEYXRlX251bWAsIG5hbWVzX3RvID0gInZhcmlhYmxlIiwgdmFsdWVzX3RvID0gInZhbHVlcyIpICU+JSAKICBnZ3Bsb3QoYWVzKGBEYXRlX251bWAsIGB2YWx1ZXNgKSkgKyBnZW9tX2xpbmUoKSArIGZhY2V0X3dyYXAodmFycyhgdmFyaWFibGVgKSwgbmNvbCA9IDEsIHNjYWxlcyA9ICJmcmVlX3kiKSArIHRoZW1lX3dzaigpCmBgYAoKYGBge3J9Ck1hc3RlciAlPiUgCiAgICBzZWxlY3QoYFByaWNlYCwgYERhdGVfbnVtYCxgRmxvdyBvdXQgZXhjaGFuZ2VzLCBVU0RgLCBgTWVhbiBGZWUgcGVyIFRyYW5zYWN0aW9uLCBVU0RgKSAlPiUKICBwaXZvdF9sb25nZXIoY29scz0tYERhdGVfbnVtYCwgbmFtZXNfdG8gPSAidmFyaWFibGUiLCB2YWx1ZXNfdG8gPSAidmFsdWVzIikgJT4lIAogIGdncGxvdChhZXMoYERhdGVfbnVtYCwgYHZhbHVlc2ApKSArIGdlb21fbGluZSgpICsgZmFjZXRfd3JhcCh2YXJzKGB2YXJpYWJsZWApLCBuY29sID0gMSwgc2NhbGVzID0gImZyZWVfeSIpICsgdGhlbWVfd3NqKCkKYGBgCgojIyMgRGF0YSBEaWN0aW9uYXJ5CgpTcGVudCBPdXRwdXQgUHJvZml0IFJhdGlvIChTT1BSKTogUHJpY2UgUGFpZCAvIFByaWNlIFNvbGQKR2FzIExpbWl0IEJsb2NrLCBNZWFuOiBTdW0gZ2FzIGxpbWl0IG9mIGFsbCBibG9jayBmcm9tIHRoYXQgaW50ZXJ2YWwKQWRqdXN0ZWQgRG9ybWFuY3kgRmxvdzogTWFya2V0IENhcC9Bbm51YWxpemVkIERvcm1hbmN5IFZhbHVlCkNvaW4gWWVhcnMgQWN0aXZlOiBDb2lucyBUcmFuc2FjdGVkIC8gIyBvZiBEYXlzIFNpbmNlIExhc3QgVHJhbnNhY3RlZApOVlQsIEFkanVzdDogTWFya2V0IENhcC9UcmFuc2ZlciBWb2x1bWUKCgojIyMgU3VwcGxlbWVudGFyeSBNb2RlbAoKIyMjIyBTVk0gLSBDbGFzc2lmaWNhdGlvbiAKCldlIGFsc28gY2Fycnkgb3V0IHN1cHBsZW1lbnRhcnkgYW5hbHlzaXMgd2l0aCBTdXBwb3J0IFZlY3RvciBNYWNoaW5lcyBiZWNhdXNlIG9mIHRoZWlyIGFkdmFudGFnZXMgaW4gY2xhc3NpZmljYXRpb24gdGFza3MuIFdlIG9ic2VydmUgdGhhdCBmb3IgdGhlIG1haW4gU3VwcG9ydCB2ZWN0b3IgbWFjaGluZSBtb2RlbCB3ZSBidWlsdCwgdGhlIHByZWRpY3Rpb24gYWNjdXJhY3kgZG9lcyBub3QgZGlmZmVyIGJ5IGEgY29uc2lkZXJhYmxlIHJhdGUgZnJvbSB0aGUgdGhyZWUgbW9kZWxzIHdlIGhhdmUgdXNlZCBhYm92ZS4gCgpgYGB7cn0KTWFzdGVyXzIgPC0gTWFzdGVyICU+JQogICAgICAgICAgICBtdXRhdGUoIlByaWNlRGlyZWN0aW9uIiA9IGZhY3RvcihpZmVsc2UoUHJpY2VEaXJlY3Rpb24gPT0gIjEiIHwgUHJpY2VEaXJlY3Rpb24gIT0gIjAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlByaWNlX0luY3JlYXNlIiwgIlByaWNlX0RlY3JlYXNlIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJQcmljZV9JbmNyZWFzZSIsICJQcmljZV9EZWNyZWFzZSIpKSkKYGBgCgpgYGB7cn0KTWFzdGVyX1NwbGl0MiA8LSBpbml0aWFsX3NwbGl0KE1hc3Rlcl8yLCBwcm9wID0gMC43NSkKCk1hc3Rlcl9UcmFpbmluZzIgPC0gdHJhaW5pbmcoTWFzdGVyX1NwbGl0MikKTWFzdGVyX1Rlc3RpbmcyIDwtIHRlc3RpbmcoTWFzdGVyX1NwbGl0MikKYGBgCgpgYGB7cn0KYnRjX2N2MiA8LSB2Zm9sZF9jdihNYXN0ZXJfVHJhaW5pbmcyLAogICAgICAgICAgICAgICAgICAgdiA9IDEwKQpgYGAKCmBgYHtyfQpzdm1fcmVjIDwtIAogIHJlY2lwZShQcmljZURpcmVjdGlvbiB+IC4sIGRhdGEgPSBNYXN0ZXJfMikgCgpzdm1fc3BlYyA8LQogIHN2bV9yYmYoY29zdCA9IHR1bmUoKSwgcmJmX3NpZ21hID0gdHVuZSgpKSAlPiUKICBzZXRfbW9kZSgiY2xhc3NpZmljYXRpb24iKSAlPiUKICBzZXRfZW5naW5lKCJrZXJubGFiIikKYGBgCgpgYGB7cn0Kc3ZtX3dmbG93IDwtIAogIHdvcmtmbG93KCkgJT4lIAogIGFkZF9tb2RlbChzdm1fc3BlYykgJT4lIAogIGFkZF9yZWNpcGUoc3ZtX3JlYykKYGBgCgpgYGB7cn0KY29zdCgpCnJiZl9zaWdtYSgpCmBgYAoKYGBge3J9CnN2bV9wYXJhbSA8LSAKICBzdm1fd2Zsb3cgJT4lIAogIHBhcmFtZXRlcnMoKSAlPiUgCiAgdXBkYXRlKHJiZl9zaWdtYSA9IHJiZl9zaWdtYShjKC03LCAtMSkpKQoKc3RhcnRfZ3JpZCA8LSAKICBzdm1fcGFyYW0gJT4lIAogIHVwZGF0ZSgKICAgIGNvc3QgPSBjb3N0KGMoLTYsIDEpKSwKICAgIHJiZl9zaWdtYSA9IHJiZl9zaWdtYShjKC02LCAtNCkpKSAlPiUgCiAgZ3JpZF9yZWd1bGFyKGxldmVscyA9IDIpCgpzZXQuc2VlZCg0NTYpCgpzdm1faW5pdGlhbCA8LSAKICBzdm1fd2Zsb3cgJT4lIAogIHR1bmVfZ3JpZChyZXNhbXBsZXMgPSBidGNfY3YyLCAKICAgICAgICAgICAgZ3JpZCA9IHN0YXJ0X2dyaWQsIAogICAgICAgICAgICBjb250cm9sID0gY29udHJvbF9zdGFja19ncmlkKCkpCmBgYAoKYGBge3J9CmNvbGxlY3RfbWV0cmljcyhzdm1faW5pdGlhbCkKYGBgCgoKYGBge3J9CmJlc3RfcGFyYW1fc3ZtIDwtIHN2bV9pbml0aWFsICU+JQogICAgICAgICAgICAgIHNlbGVjdF9iZXN0KG1ldHJpYyA9ICJhY2N1cmFjeSIpCmJlc3RfcGFyYW1fc3ZtCmBgYAoKIyMjIyMgQmF5ZXNpYW4gT3B0aW1pemF0aW9uIGZvciBTVk0gCgpgYGB7cn0KY3RybCA8LSBjb250cm9sX2JheWVzKHZlcmJvc2UgPSBUUlVFKQoKc2V0LnNlZWQoNDIwKQoKc3ZtX2JvIDwtCiAgc3ZtX3dmbG93ICU+JQogIHR1bmVfYmF5ZXMoCiAgICByZXNhbXBsZXMgPSBidGNfY3YyLCAKICAgIG1ldHJpY3MgPSBOVUxMLCAKICAgIGluaXRpYWwgPSBzdm1faW5pdGlhbCwgIyB0dW5lX2dyaWQgb2JqZWN0IHByb2R1Y2VkIGVhcmxpZXIKICAgIHBhcmFtX2luZm8gPSBzdm1fcGFyYW0sICMgc3BlY2lmaWVkIGVhcmxpZXIgdG9vLCB3aXRoIG91ciBuZXcgYm91bmRzIGZvciByYmZfc2lnbWEKICAgIGl0ZXIgPSAyNywgIyBtYXhpbXVtIG51bWJlciBvZiBzZWFyY2ggaXRlcmF0aW9ucwogICAgY29udHJvbCA9IGN0cmwKICApCmBgYAoKYGBge3J9CnNob3dfYmVzdChzdm1fYm8pCmBgYAoKYGBge3J9CnN2bV9ibyAlPiUgY29sbGVjdF9tZXRyaWNzKCkKYGBgCgoKCgoK